lmnr 0.6.9__tar.gz → 0.6.10__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.
- {lmnr-0.6.9 → lmnr-0.6.10}/PKG-INFO +1 -1
- {lmnr-0.6.9 → lmnr-0.6.10}/pyproject.toml +1 -1
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/cli.py +16 -3
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/__init__.py +2 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +4 -2
- lmnr-0.6.10/src/lmnr/opentelemetry_lib/tracing/__init__.py +162 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/asynchronous/resources/evals.py +4 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/synchronous/resources/evals.py +4 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/evaluations.py +10 -15
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/laminar.py +7 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/version.py +1 -1
- lmnr-0.6.9/src/lmnr/opentelemetry_lib/tracing/__init__.py +0 -157
- {lmnr-0.6.9 → lmnr-0.6.10}/LICENSE +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/README.md +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/__init__.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/.flake8 +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/decorators/__init__.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/config.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/utils.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/tracing/attributes.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/tracing/context_properties.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/tracing/exporter.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/tracing/instruments.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/tracing/processor.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/tracing/tracer.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/utils/__init__.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/utils/json_encoder.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/opentelemetry_lib/utils/package_check.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/py.typed +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/__init__.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/browser/__init__.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/browser/browser_use_otel.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/browser/patchright_otel.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/browser/playwright_otel.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/browser/pw_utils.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/browser/rrweb/rrweb.umd.min.cjs +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/browser/utils.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/asynchronous/async_client.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/asynchronous/resources/__init__.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/asynchronous/resources/agent.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/asynchronous/resources/base.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/asynchronous/resources/browser_events.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/asynchronous/resources/tags.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/synchronous/resources/__init__.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/synchronous/resources/agent.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/synchronous/resources/base.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/synchronous/resources/browser_events.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/synchronous/resources/tags.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/client/synchronous/sync_client.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/datasets.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/decorators.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/eval_control.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/log.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/types.py +0 -0
- {lmnr-0.6.9 → lmnr-0.6.10}/src/lmnr/sdk/utils.py +0 -0
@@ -100,9 +100,14 @@ async def run_evaluation(args):
|
|
100
100
|
sys.modules[name] = mod
|
101
101
|
|
102
102
|
spec.loader.exec_module(mod)
|
103
|
-
evaluations
|
104
|
-
|
105
|
-
|
103
|
+
evaluations = []
|
104
|
+
try:
|
105
|
+
evaluations: list[Evaluation] | None = EVALUATION_INSTANCES.get()
|
106
|
+
if evaluations is None:
|
107
|
+
raise LookupError()
|
108
|
+
# may be raised by `get()` or manually by us above
|
109
|
+
except LookupError:
|
110
|
+
log_evaluation_instance_not_found()
|
106
111
|
if args.continue_on_error:
|
107
112
|
continue
|
108
113
|
return
|
@@ -130,6 +135,14 @@ async def run_evaluation(args):
|
|
130
135
|
PREPARE_ONLY.reset(prep_token)
|
131
136
|
|
132
137
|
|
138
|
+
def log_evaluation_instance_not_found():
|
139
|
+
LOG.warning(
|
140
|
+
"Evaluation instance not found. "
|
141
|
+
"`evaluate` must be called at the top level of the file, "
|
142
|
+
"not inside a function when running evaluations from the CLI."
|
143
|
+
)
|
144
|
+
|
145
|
+
|
133
146
|
def cli():
|
134
147
|
parser = ArgumentParser(
|
135
148
|
prog="lmnr",
|
@@ -11,7 +11,7 @@ from .utils import (
|
|
11
11
|
from langchain_core.runnables.graph import Graph
|
12
12
|
from opentelemetry.trace import Tracer
|
13
13
|
from wrapt import wrap_function_wrapper
|
14
|
-
from opentelemetry.trace import get_tracer
|
14
|
+
from opentelemetry.trace import get_tracer
|
15
15
|
|
16
16
|
from lmnr.opentelemetry_lib.tracing.context_properties import (
|
17
17
|
update_association_properties,
|
@@ -81,7 +81,9 @@ async def async_wrap_pregel_stream(
|
|
81
81
|
"langgraph.nodes": json.dumps(nodes),
|
82
82
|
},
|
83
83
|
)
|
84
|
-
|
84
|
+
|
85
|
+
async for item in wrapped(*args, **kwargs):
|
86
|
+
yield item
|
85
87
|
|
86
88
|
|
87
89
|
class LanggraphInstrumentor(BaseInstrumentor):
|
@@ -0,0 +1,162 @@
|
|
1
|
+
import atexit
|
2
|
+
import logging
|
3
|
+
import threading
|
4
|
+
|
5
|
+
from lmnr.opentelemetry_lib.tracing.processor import LaminarSpanProcessor
|
6
|
+
from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
|
7
|
+
from lmnr.sdk.client.synchronous.sync_client import LaminarClient
|
8
|
+
from lmnr.sdk.log import VerboseColorfulFormatter
|
9
|
+
from lmnr.opentelemetry_lib.tracing.instruments import (
|
10
|
+
Instruments,
|
11
|
+
init_instrumentations,
|
12
|
+
)
|
13
|
+
|
14
|
+
from opentelemetry import trace
|
15
|
+
from opentelemetry.instrumentation.threading import ThreadingInstrumentor
|
16
|
+
from opentelemetry.sdk.resources import Resource
|
17
|
+
from opentelemetry.sdk.trace import TracerProvider, SpanProcessor
|
18
|
+
from opentelemetry.sdk.trace.export import SpanExporter
|
19
|
+
|
20
|
+
TRACER_NAME = "lmnr.tracer"
|
21
|
+
|
22
|
+
MAX_EVENTS_OR_ATTRIBUTES_PER_SPAN = 5000
|
23
|
+
|
24
|
+
|
25
|
+
class TracerWrapper(object):
|
26
|
+
resource_attributes: dict = {}
|
27
|
+
enable_content_tracing: bool = True
|
28
|
+
_lock = threading.Lock()
|
29
|
+
_tracer_provider: TracerProvider | None = None
|
30
|
+
_logger: logging.Logger
|
31
|
+
_client: LaminarClient
|
32
|
+
_async_client: AsyncLaminarClient
|
33
|
+
_resource: Resource
|
34
|
+
_span_processor: SpanProcessor
|
35
|
+
|
36
|
+
def __new__(
|
37
|
+
cls,
|
38
|
+
disable_batch=False,
|
39
|
+
exporter: SpanExporter | None = None,
|
40
|
+
instruments: set[Instruments] | None = None,
|
41
|
+
block_instruments: set[Instruments] | None = None,
|
42
|
+
base_url: str = "https://api.lmnr.ai",
|
43
|
+
port: int = 8443,
|
44
|
+
http_port: int = 443,
|
45
|
+
project_api_key: str | None = None,
|
46
|
+
max_export_batch_size: int | None = None,
|
47
|
+
force_http: bool = False,
|
48
|
+
timeout_seconds: int = 10,
|
49
|
+
set_global_tracer_provider: bool = True,
|
50
|
+
otel_logger_level: int = logging.ERROR,
|
51
|
+
) -> "TracerWrapper":
|
52
|
+
# Silence some opentelemetry warnings
|
53
|
+
logging.getLogger("opentelemetry.trace").setLevel(otel_logger_level)
|
54
|
+
|
55
|
+
base_http_url = f"{base_url}:{http_port}"
|
56
|
+
with cls._lock:
|
57
|
+
if not hasattr(cls, "instance"):
|
58
|
+
cls._initialize_logger(cls)
|
59
|
+
obj = super(TracerWrapper, cls).__new__(cls)
|
60
|
+
|
61
|
+
obj._client = LaminarClient(
|
62
|
+
base_url=base_http_url,
|
63
|
+
project_api_key=project_api_key,
|
64
|
+
)
|
65
|
+
obj._async_client = AsyncLaminarClient(
|
66
|
+
base_url=base_http_url,
|
67
|
+
project_api_key=project_api_key,
|
68
|
+
)
|
69
|
+
|
70
|
+
obj._resource = Resource(attributes=TracerWrapper.resource_attributes)
|
71
|
+
|
72
|
+
obj._span_processor = LaminarSpanProcessor(
|
73
|
+
base_url=base_url,
|
74
|
+
api_key=project_api_key,
|
75
|
+
port=http_port if force_http else port,
|
76
|
+
exporter=exporter,
|
77
|
+
max_export_batch_size=max_export_batch_size,
|
78
|
+
timeout_seconds=timeout_seconds,
|
79
|
+
force_http=force_http,
|
80
|
+
disable_batch=disable_batch,
|
81
|
+
)
|
82
|
+
|
83
|
+
lmnr_provider = TracerProvider(resource=obj._resource)
|
84
|
+
global_provider = trace.get_tracer_provider()
|
85
|
+
if set_global_tracer_provider and isinstance(
|
86
|
+
global_provider, trace.ProxyTracerProvider
|
87
|
+
):
|
88
|
+
trace.set_tracer_provider(lmnr_provider)
|
89
|
+
|
90
|
+
obj._tracer_provider = lmnr_provider
|
91
|
+
|
92
|
+
obj._tracer_provider.add_span_processor(obj._span_processor)
|
93
|
+
|
94
|
+
# This is not a real instrumentation and does not generate telemetry
|
95
|
+
# data, but it is required to ensure that OpenTelemetry context
|
96
|
+
# propagation is enabled.
|
97
|
+
# See the README at:
|
98
|
+
# https://pypi.org/project/opentelemetry-instrumentation-threading/
|
99
|
+
ThreadingInstrumentor().instrument()
|
100
|
+
|
101
|
+
init_instrumentations(
|
102
|
+
tracer_provider=obj._tracer_provider,
|
103
|
+
instruments=instruments,
|
104
|
+
block_instruments=block_instruments,
|
105
|
+
client=obj._client,
|
106
|
+
async_client=obj._async_client,
|
107
|
+
)
|
108
|
+
|
109
|
+
cls.instance = obj
|
110
|
+
|
111
|
+
# Force flushes for debug environments (e.g. local development)
|
112
|
+
atexit.register(obj.exit_handler)
|
113
|
+
|
114
|
+
return cls.instance
|
115
|
+
|
116
|
+
def exit_handler(self):
|
117
|
+
if isinstance(self._span_processor, LaminarSpanProcessor):
|
118
|
+
self._span_processor.clear()
|
119
|
+
self.flush()
|
120
|
+
|
121
|
+
def _initialize_logger(self):
|
122
|
+
self._logger = logging.getLogger(__name__)
|
123
|
+
console_log_handler = logging.StreamHandler()
|
124
|
+
console_log_handler.setFormatter(VerboseColorfulFormatter())
|
125
|
+
self._logger.addHandler(console_log_handler)
|
126
|
+
|
127
|
+
@staticmethod
|
128
|
+
def set_static_params(
|
129
|
+
resource_attributes: dict,
|
130
|
+
enable_content_tracing: bool,
|
131
|
+
) -> None:
|
132
|
+
TracerWrapper.resource_attributes = resource_attributes
|
133
|
+
TracerWrapper.enable_content_tracing = enable_content_tracing
|
134
|
+
|
135
|
+
@classmethod
|
136
|
+
def verify_initialized(cls) -> bool:
|
137
|
+
with cls._lock:
|
138
|
+
return hasattr(cls, "instance") and hasattr(cls.instance, "_span_processor")
|
139
|
+
|
140
|
+
@classmethod
|
141
|
+
def clear(cls):
|
142
|
+
if not cls.verify_initialized():
|
143
|
+
return
|
144
|
+
# Any state cleanup. Now used in between tests
|
145
|
+
if isinstance(cls.instance._span_processor, LaminarSpanProcessor):
|
146
|
+
cls.instance._span_processor.clear()
|
147
|
+
|
148
|
+
def shutdown(self):
|
149
|
+
if self._tracer_provider is None:
|
150
|
+
return
|
151
|
+
self._tracer_provider.shutdown()
|
152
|
+
|
153
|
+
def flush(self):
|
154
|
+
if not hasattr(self, "_span_processor"):
|
155
|
+
self._logger.warning("TracerWrapper not fully initialized, cannot flush")
|
156
|
+
return False
|
157
|
+
return self._span_processor.force_flush()
|
158
|
+
|
159
|
+
def get_tracer(self):
|
160
|
+
if self._tracer_provider is None:
|
161
|
+
return trace.get_tracer_provider().get_tracer(TRACER_NAME)
|
162
|
+
return self._tracer_provider.get_tracer(TRACER_NAME)
|
@@ -33,6 +33,10 @@ class AsyncEvals(BaseAsyncResource):
|
|
33
33
|
},
|
34
34
|
headers=self._headers(),
|
35
35
|
)
|
36
|
+
if response.status_code != 200:
|
37
|
+
if response.status_code == 401:
|
38
|
+
raise ValueError("Unauthorized. Please check your project API key.")
|
39
|
+
raise ValueError(f"Error initializing evaluation: {response.text}")
|
36
40
|
resp_json = response.json()
|
37
41
|
return InitEvaluationResponse.model_validate(resp_json)
|
38
42
|
|
@@ -35,6 +35,10 @@ class Evals(BaseResource):
|
|
35
35
|
},
|
36
36
|
headers=self._headers(),
|
37
37
|
)
|
38
|
+
if response.status_code != 200:
|
39
|
+
if response.status_code == 401:
|
40
|
+
raise ValueError("Unauthorized. Please check your project API key.")
|
41
|
+
raise ValueError(f"Error initializing evaluation: {response.text}")
|
38
42
|
resp_json = response.json()
|
39
43
|
return InitEvaluationResponse.model_validate(resp_json)
|
40
44
|
|
@@ -199,11 +199,11 @@ class Evaluation:
|
|
199
199
|
self.base_http_url = f"{base_url}:{http_port or 443}"
|
200
200
|
|
201
201
|
api_key = project_api_key or from_env("LMNR_PROJECT_API_KEY")
|
202
|
-
if not api_key:
|
202
|
+
if not api_key and not L.is_initialized():
|
203
203
|
raise ValueError(
|
204
|
-
"Please
|
205
|
-
"
|
206
|
-
"
|
204
|
+
"Please pass the project API key to `evaluate`"
|
205
|
+
" or set the LMNR_PROJECT_API_KEY environment variable"
|
206
|
+
" in your environment or .env file"
|
207
207
|
)
|
208
208
|
self.project_api_key = api_key
|
209
209
|
|
@@ -212,17 +212,12 @@ class Evaluation:
|
|
212
212
|
base_url=L.get_base_http_url(),
|
213
213
|
project_api_key=L.get_project_api_key(),
|
214
214
|
)
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
return
|
215
|
+
else:
|
216
|
+
self.client = AsyncLaminarClient(
|
217
|
+
base_url=self.base_http_url,
|
218
|
+
project_api_key=self.project_api_key,
|
219
|
+
)
|
221
220
|
|
222
|
-
self.client = AsyncLaminarClient(
|
223
|
-
base_url=self.base_http_url,
|
224
|
-
project_api_key=self.project_api_key,
|
225
|
-
)
|
226
221
|
L.initialize(
|
227
222
|
project_api_key=project_api_key,
|
228
223
|
base_url=base_url,
|
@@ -261,7 +256,7 @@ class Evaluation:
|
|
261
256
|
except Exception as e:
|
262
257
|
self.reporter.stopWithError(e)
|
263
258
|
await self._shutdown()
|
264
|
-
|
259
|
+
raise
|
265
260
|
|
266
261
|
average_scores = get_average_scores(result_datapoints)
|
267
262
|
self.reporter.stop(average_scores, evaluation.projectId, evaluation.id)
|
@@ -114,6 +114,12 @@ class Laminar:
|
|
114
114
|
Raises:
|
115
115
|
ValueError: If project API key is not set
|
116
116
|
"""
|
117
|
+
if cls.is_initialized():
|
118
|
+
cls.__logger.info(
|
119
|
+
"Laminar is already initialized. Skipping initialization."
|
120
|
+
)
|
121
|
+
return
|
122
|
+
|
117
123
|
cls.__project_api_key = project_api_key or from_env("LMNR_PROJECT_API_KEY")
|
118
124
|
if not cls.__project_api_key:
|
119
125
|
raise ValueError(
|
@@ -691,6 +697,7 @@ class Laminar:
|
|
691
697
|
def shutdown(cls):
|
692
698
|
if cls.is_initialized():
|
693
699
|
TracerManager.shutdown()
|
700
|
+
cls.__initialized = False
|
694
701
|
|
695
702
|
@classmethod
|
696
703
|
def set_span_tags(cls, tags: list[str]):
|
@@ -1,157 +0,0 @@
|
|
1
|
-
import atexit
|
2
|
-
import logging
|
3
|
-
|
4
|
-
from lmnr.opentelemetry_lib.tracing.processor import LaminarSpanProcessor
|
5
|
-
from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
|
6
|
-
from lmnr.sdk.client.synchronous.sync_client import LaminarClient
|
7
|
-
from lmnr.sdk.log import VerboseColorfulFormatter
|
8
|
-
from lmnr.opentelemetry_lib.tracing.instruments import (
|
9
|
-
Instruments,
|
10
|
-
init_instrumentations,
|
11
|
-
)
|
12
|
-
|
13
|
-
from opentelemetry import trace
|
14
|
-
from opentelemetry.instrumentation.threading import ThreadingInstrumentor
|
15
|
-
from opentelemetry.sdk.resources import Resource
|
16
|
-
from opentelemetry.sdk.trace import TracerProvider, SpanProcessor
|
17
|
-
from opentelemetry.sdk.trace.export import SpanExporter
|
18
|
-
|
19
|
-
module_logger = logging.getLogger(__name__)
|
20
|
-
console_log_handler = logging.StreamHandler()
|
21
|
-
console_log_handler.setFormatter(VerboseColorfulFormatter())
|
22
|
-
module_logger.addHandler(console_log_handler)
|
23
|
-
|
24
|
-
|
25
|
-
TRACER_NAME = "lmnr.tracer"
|
26
|
-
|
27
|
-
MAX_EVENTS_OR_ATTRIBUTES_PER_SPAN = 5000
|
28
|
-
|
29
|
-
|
30
|
-
class TracerWrapper(object):
|
31
|
-
resource_attributes: dict = {}
|
32
|
-
enable_content_tracing: bool = True
|
33
|
-
__tracer_provider: TracerProvider | None = None
|
34
|
-
__logger: logging.Logger
|
35
|
-
__client: LaminarClient
|
36
|
-
__async_client: AsyncLaminarClient
|
37
|
-
__resource: Resource
|
38
|
-
__span_processor: SpanProcessor
|
39
|
-
|
40
|
-
def __new__(
|
41
|
-
cls,
|
42
|
-
disable_batch=False,
|
43
|
-
exporter: SpanExporter | None = None,
|
44
|
-
instruments: set[Instruments] | None = None,
|
45
|
-
block_instruments: set[Instruments] | None = None,
|
46
|
-
base_url: str = "https://api.lmnr.ai",
|
47
|
-
port: int = 8443,
|
48
|
-
http_port: int = 443,
|
49
|
-
project_api_key: str | None = None,
|
50
|
-
max_export_batch_size: int | None = None,
|
51
|
-
force_http: bool = False,
|
52
|
-
timeout_seconds: int = 10,
|
53
|
-
set_global_tracer_provider: bool = True,
|
54
|
-
otel_logger_level: int = logging.ERROR,
|
55
|
-
) -> "TracerWrapper":
|
56
|
-
# Silence some opentelemetry warnings
|
57
|
-
logging.getLogger("opentelemetry.trace").setLevel(otel_logger_level)
|
58
|
-
|
59
|
-
base_http_url = f"{base_url}:{http_port}"
|
60
|
-
cls._initialize_logger(cls)
|
61
|
-
if not hasattr(cls, "instance"):
|
62
|
-
obj = cls.instance = super(TracerWrapper, cls).__new__(cls)
|
63
|
-
|
64
|
-
obj.__client = LaminarClient(
|
65
|
-
base_url=base_http_url,
|
66
|
-
project_api_key=project_api_key,
|
67
|
-
)
|
68
|
-
obj.__async_client = AsyncLaminarClient(
|
69
|
-
base_url=base_http_url,
|
70
|
-
project_api_key=project_api_key,
|
71
|
-
)
|
72
|
-
|
73
|
-
obj.__resource = Resource(attributes=TracerWrapper.resource_attributes)
|
74
|
-
|
75
|
-
obj.__span_processor = LaminarSpanProcessor(
|
76
|
-
base_url=base_url,
|
77
|
-
api_key=project_api_key,
|
78
|
-
port=http_port if force_http else port,
|
79
|
-
exporter=exporter,
|
80
|
-
max_export_batch_size=max_export_batch_size,
|
81
|
-
timeout_seconds=timeout_seconds,
|
82
|
-
force_http=force_http,
|
83
|
-
disable_batch=disable_batch,
|
84
|
-
)
|
85
|
-
|
86
|
-
lmnr_provider = TracerProvider(resource=obj.__resource)
|
87
|
-
global_provider = trace.get_tracer_provider()
|
88
|
-
if set_global_tracer_provider and isinstance(
|
89
|
-
global_provider, trace.ProxyTracerProvider
|
90
|
-
):
|
91
|
-
trace.set_tracer_provider(lmnr_provider)
|
92
|
-
|
93
|
-
obj.__tracer_provider = lmnr_provider
|
94
|
-
|
95
|
-
obj.__tracer_provider.add_span_processor(obj.__span_processor)
|
96
|
-
|
97
|
-
# This is not a real instrumentation and does not generate telemetry
|
98
|
-
# data, but it is required to ensure that OpenTelemetry context
|
99
|
-
# propagation is enabled.
|
100
|
-
# See the README at:
|
101
|
-
# https://pypi.org/project/opentelemetry-instrumentation-threading/
|
102
|
-
ThreadingInstrumentor().instrument()
|
103
|
-
|
104
|
-
init_instrumentations(
|
105
|
-
tracer_provider=obj.__tracer_provider,
|
106
|
-
instruments=instruments,
|
107
|
-
block_instruments=block_instruments,
|
108
|
-
client=obj.__client,
|
109
|
-
async_client=obj.__async_client,
|
110
|
-
)
|
111
|
-
|
112
|
-
# Force flushes for debug environments (e.g. local development)
|
113
|
-
atexit.register(obj.exit_handler)
|
114
|
-
|
115
|
-
return cls.instance
|
116
|
-
|
117
|
-
def exit_handler(self):
|
118
|
-
if isinstance(self.__span_processor, LaminarSpanProcessor):
|
119
|
-
self.__span_processor.clear()
|
120
|
-
self.flush()
|
121
|
-
|
122
|
-
def _initialize_logger(self):
|
123
|
-
self.__logger = logging.getLogger(__name__)
|
124
|
-
console_log_handler = logging.StreamHandler()
|
125
|
-
console_log_handler.setFormatter(VerboseColorfulFormatter())
|
126
|
-
self.__logger.addHandler(console_log_handler)
|
127
|
-
|
128
|
-
@staticmethod
|
129
|
-
def set_static_params(
|
130
|
-
resource_attributes: dict,
|
131
|
-
enable_content_tracing: bool,
|
132
|
-
) -> None:
|
133
|
-
TracerWrapper.resource_attributes = resource_attributes
|
134
|
-
TracerWrapper.enable_content_tracing = enable_content_tracing
|
135
|
-
|
136
|
-
@classmethod
|
137
|
-
def verify_initialized(cls) -> bool:
|
138
|
-
return hasattr(cls, "instance")
|
139
|
-
|
140
|
-
@classmethod
|
141
|
-
def clear(cls):
|
142
|
-
# Any state cleanup. Now used in between tests
|
143
|
-
if isinstance(cls.instance.__span_processor, LaminarSpanProcessor):
|
144
|
-
cls.instance.__span_processor.clear()
|
145
|
-
|
146
|
-
def shutdown(self):
|
147
|
-
if self.__tracer_provider is None:
|
148
|
-
return
|
149
|
-
self.__tracer_provider.shutdown()
|
150
|
-
|
151
|
-
def flush(self):
|
152
|
-
return self.__span_processor.force_flush()
|
153
|
-
|
154
|
-
def get_tracer(self):
|
155
|
-
if self.__tracer_provider is None:
|
156
|
-
return trace.get_tracer_provider().get_tracer(TRACER_NAME)
|
157
|
-
return self.__tracer_provider.get_tracer(TRACER_NAME)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|