Coverage for anaconda_opentelemetry/config.py: 99%
316 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-01 20:06 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-01 20:06 +0000
1# -*- coding: utf-8 -*-
2# SPDX-FileCopyrightText: 2025 Anaconda, Inc
3# SPDX-License-Identifier: Apache-2.0
5# config.py
6"""
7Anaconda Telemetry - Configuration Module
9This module provides the configuration setting from a file or a dictionary (or both)
10"""
12from typing import Dict, Any, List
13import re, os, grpc, warnings, functools
15"""
16Configuration class to supply settings for Anaconda Telemetry.
17It allows loading configuration from a JSON or YAML file, or from a dictionary.
18It validates the format of endpoints and ensures they conform to the expected structure.
19"""
22def deprecated(func):
23 # This is a decorator to mark functions as deprecated.
24 @functools.wraps(func)
25 def wrapper(*args, **kwargs):
26 warnings.warn(
27 f"{func.__name__} is deprecated and will be removed in a future version.",
28 category=DeprecationWarning,
29 stacklevel=2
30 )
31 return func(*args, **kwargs)
32 return wrapper
35class Configuration:
36 """
37 Configuration class to supply settings for Anaconda Telemetry. For environment variables make these capitalized and
38 prepend with 'ATEL\\_' and remove suffix '\\_NAME'. For example, the environment variable for the default endpoint would
39 be 'ATEL_DEFAULT_ENDPOINT'. The environment variable for the logging endpoint would be 'ATEL_LOGGING_ENDPOINT'. The bool
40 values can be represented by "1", "yes", "true" case-insensitive and all other values are considered False.
42 - DEFAULT_ENDPOINT_NAME - Name for the default endpoint in the configuration files or dictionaries passed into this class.
43 - LOGGING_ENDPOINT_NAME - Name for the logging endpoint in the configuration files or dictionaries passed into this class.
44 - TRACING_ENDPOINT_NAME - Name for the tracing endpoint in the configuration files or dictionaries passed into this class.
45 - METRICS_ENDPOINT_NAME - Name for the metrics endpoint in the configuration files or dictionaries passed into this class.
46 - USE_CONSOLE_EXPORTER_NAME - Name for the console exporter flag in the configuration files or dictionaries passed into this class.
47 - DEFAULT_AUTH_TOKEN_NAME - Name for the default authentication token in the configuration files or dictionaries passed into this class.
48 - LOGGING_AUTH_TOKEN_NAME - Name for the logging authentication token in the configuration files or dictionaries passed into this class.
49 - TRACING_AUTH_TOKEN_NAME - Name for the tracing authentication token in the configuration files or dictionaries passed into this class.
50 - METRICS_AUTH_TOKEN_NAME - Name for the metrics authentication token in the configuration files or dictionaries passed into this class.
51 - METRICS_EXPORT_INTERVAL_MS_NAME - Name for the metrics export interval in milliseconds in the configuration files or dictionaries passed into this class.
52 - TRACING_EXPORT_INTERVAL_MS_NAME - Name for the tracing export interval in milliseconds in the configuration files or dictionaries passed into this class.
53 - LOGGING_LEVEL_NAME - Name for the logging level in the configuration files or dictionaries passed into this class.
54 - SESSION_ENTROPY_VALUE_NAME - Name for the session entropy value in the configuration files or dictionaries passed into this class.
55 - TLS_PRIVATE_CA_CERT_FILE_NAME - File name for the TLS private CA certificate in the configuration files or dictionaries passed into this class.
56 - SKIP_INTERNET_CHECK_NAME - If you are running in an environment that does not have access to the internet, set this to True.
57 - USE_CUMULATIVE_METRICS_NAME - If aggregating data in the client is required for Counter, or Histogram set this to a True state.
58 - PROXY_URL_NAME - Used to set the proxy for telemetry exporters in this package
59 - SHUTDOWN_ON_EXIT_NAME - If True (default), providers register atexit handlers that flush on interpreter exit. If False, the caller must manually call shutdown_telemetry() or flush_telemetry() before the process exits.
60 - VERBOSE_EXPORT_ERRORS_NAME - If False (default), OpenTelemetry export errors are suppressed. If True, export errors such as "Transient error" will be logged.
61 - DISABLE_SESSION_ID_NAME - If True, disables automatic session ID generation. If False (default), a session ID is generated and added to telemetry.
63 To initializes the Configuration instance.
65 config = Configuration(default_endpoint='example.com:4317').set_auth_token('<token_here>')
67 Args:
68 default_endpoint (str): Default endpoint in the form '<IPv4|domain_name>:<port>'.
69 config_dict (Dict[str,Any], optional): Optional dictionary containing configuration settings.
70 """
71 __PREFIX__ = 'ATEL_'
73 DEFAULT_ENDPOINT_NAME = 'default_endpoint'
74 LOGGING_ENDPOINT_NAME = 'logging_endpoint'
75 TRACING_ENDPOINT_NAME = 'tracing_endpoint'
76 METRICS_ENDPOINT_NAME = 'metrics_endpoint'
77 USE_CONSOLE_EXPORTER_NAME = 'use_console_exporter'
78 DEFAULT_AUTH_TOKEN_NAME = 'default_auth_token'
79 LOGGING_AUTH_TOKEN_NAME = 'logging_auth_token'
80 TRACING_AUTH_TOKEN_NAME = 'tracing_auth_token'
81 METRICS_AUTH_TOKEN_NAME = 'metrics_auth_token'
82 METRICS_EXPORT_INTERVAL_MS_NAME = 'metrics_export_interval_ms'
83 TRACING_EXPORT_INTERVAL_MS_NAME = 'tracing_export_interval_ms'
84 LOGGING_LEVEL_NAME = 'logging_level'
85 SESSION_ENTROPY_VALUE_NAME = 'session_entropy_value'
86 DEFAULT_CA_CERT_NAME = 'default_credentials'
87 LOGGING_CA_CERT_NAME = 'logging_credentials'
88 TRACING_CA_CERT_NAME = 'tracing_credentials'
89 METRICS_CA_CERT_NAME = 'metrics_credentials'
90 SKIP_INTERNET_CHECK_NAME = 'skip_internet_check'
91 USE_CUMULATIVE_METRICS_NAME = 'use_cumulative_metrics'
92 PROXY_URL_NAME = 'proxy_url'
93 SHUTDOWN_ON_EXIT_NAME = 'shutdown_on_exit'
94 VERBOSE_EXPORT_ERRORS_NAME = 'verbose_export_errors'
95 DISABLE_SESSION_ID_NAME = 'disable_session_id'
97 _base_names: List[str] = [
98 DEFAULT_ENDPOINT_NAME,
99 LOGGING_ENDPOINT_NAME,
100 TRACING_ENDPOINT_NAME,
101 METRICS_ENDPOINT_NAME,
102 USE_CONSOLE_EXPORTER_NAME,
103 DEFAULT_AUTH_TOKEN_NAME,
104 LOGGING_AUTH_TOKEN_NAME,
105 TRACING_AUTH_TOKEN_NAME,
106 METRICS_AUTH_TOKEN_NAME,
107 METRICS_EXPORT_INTERVAL_MS_NAME,
108 TRACING_EXPORT_INTERVAL_MS_NAME,
109 LOGGING_LEVEL_NAME,
110 SESSION_ENTROPY_VALUE_NAME,
111 DEFAULT_CA_CERT_NAME,
112 LOGGING_CA_CERT_NAME,
113 TRACING_CA_CERT_NAME,
114 METRICS_CA_CERT_NAME,
115 SKIP_INTERNET_CHECK_NAME,
116 USE_CUMULATIVE_METRICS_NAME,
117 PROXY_URL_NAME,
118 SHUTDOWN_ON_EXIT_NAME,
119 VERBOSE_EXPORT_ERRORS_NAME,
120 DISABLE_SESSION_ID_NAME,
121 ]
123 _endpoint_names: List[str] = [
124 DEFAULT_ENDPOINT_NAME,
125 LOGGING_ENDPOINT_NAME,
126 TRACING_ENDPOINT_NAME,
127 METRICS_ENDPOINT_NAME
128 ]
130 _credential_names: List[str] = [
131 DEFAULT_CA_CERT_NAME,
132 LOGGING_CA_CERT_NAME,
133 TRACING_CA_CERT_NAME,
134 METRICS_CA_CERT_NAME
135 ]
137 _auth_token_names: List[str] = [
138 DEFAULT_AUTH_TOKEN_NAME,
139 LOGGING_AUTH_TOKEN_NAME,
140 TRACING_AUTH_TOKEN_NAME,
141 METRICS_AUTH_TOKEN_NAME
142 ]
144 _bool_value_names: List[str] = [
145 USE_CONSOLE_EXPORTER_NAME,
146 SKIP_INTERNET_CHECK_NAME,
147 USE_CUMULATIVE_METRICS_NAME,
148 SHUTDOWN_ON_EXIT_NAME,
149 VERBOSE_EXPORT_ERRORS_NAME,
150 DISABLE_SESSION_ID_NAME,
151 ]
153 _int_value_names: List[str] = [
154 METRICS_EXPORT_INTERVAL_MS_NAME
155 ]
157 def __init__(self, default_endpoint: str = None, default_auth_token: str = None,
158 default_private_ca_cert_file: str = None, config_dict: Dict[str, Any] = {},
159 ignore_environment_variables: bool = False):
160 """
161 Creates the configuration object passed to initialize_telemetry.
163 Args:
164 default_endpoint (str): The endpoint used when not specifying a specific endpoint for a specific signal type. May be None.
165 default_auth_token (str): The default auth token use for the default_endpoint or None.
166 default_private_ca_cert_file (str): File name for the private cert file if used or None. Not used frequently.
167 config_dict (Dict[str,any]): An initialization map to configure the object in bulk or {}.
168 ignore_environment_variables (bool): If True, all ATEL_* environment variables and OTEL_SDK_DISABLED are
169 ignored. Only values passed via constructor arguments and config_dict
170 are used. Defaults to False.
172 Raises:
173 ValueError: If there is no `default_endpoint` value passed to its arguments or in the `config_dict` kwarg,
174 and no `ATEL_DEFAULT_ENDPOINT` environment variable set.
175 ValueError: Non integer value set for `ATEL_METRICS_EXPORT_INTERVAL_MS_NAME`
176 """
177 self._config: Dict[str, Any] = {}
178 self._config.update(config_dict)
180 if default_endpoint is not None:
181 endpoint = self._Endpoint(default_endpoint)
182 self._config[self.DEFAULT_ENDPOINT_NAME] = endpoint.url
184 if default_auth_token is not None:
185 self._config[self.DEFAULT_AUTH_TOKEN_NAME] = default_auth_token
187 if default_private_ca_cert_file is not None:
188 self._config[self.DEFAULT_CA_CERT_NAME] = default_private_ca_cert_file
190 # Merge environment variables into the config
191 if not ignore_environment_variables:
192 for base_name in self._base_names:
193 env_name = f"{self.__PREFIX__}{base_name.upper()}"
194 env_value = os.environ.get(env_name, None)
195 if env_value is not None:
196 self._config[base_name] = env_value.strip()
198 # Ensure default endpoint is set
199 if self.DEFAULT_ENDPOINT_NAME not in self._config.keys():
200 raise ValueError(f"A '{self.DEFAULT_ENDPOINT_NAME}' must be provided or set in the configuration.")
202 # Check environment vars for endpoints and normalize endpoints
203 self._endpoints = {}
204 for endpoint_name in self._endpoint_names:
205 if endpoint_name in self._config:
206 # set endpoint object for this signal (or default)
207 self._endpoints[endpoint_name] = self._Endpoint(self._config[endpoint_name])
208 # set endpoint config value for this signal (or default)
209 self._config[endpoint_name] = self._endpoints[endpoint_name].url
211 # Normalize bool values
212 for bool_name in self._bool_value_names:
213 if bool_name in self._config and isinstance(self._config[bool_name], str):
214 self._config[bool_name] = self._config[bool_name].lower().strip() in ['true', 'yes', '1', 'on']
216 # Special case OTEL_SDK_DISABLED...
217 if not ignore_environment_variables and \
218 os.environ.get('OTEL_SDK_DISABLED', '').lower().strip() in ['true', 'yes', '1', 'on'] and \
219 os.environ.get(self.SKIP_INTERNET_CHECK_NAME, None) is None:
220 self._config[self.SKIP_INTERNET_CHECK_NAME] = True
222 # Normalize the int values
223 for int_name in self._int_value_names:
224 if int_name in self._config and isinstance(self._config[int_name], str):
225 try:
226 self._config[int_name] = int(self._config[int_name].strip())
227 except ValueError:
228 raise ValueError(f"Invalid value for '{int_name}': {self._config[int_name]}")
230 self._metric_defs: Dict[str,Configuration._MetricInfo] = {}
232 def set_logging_endpoint(self, endpoint: str, auth_token: str = None, cert_ca_file: str = None):
233 """
234 Sets the logging endpoint. Intended for usage prior to calling initialize_telemetry(). If this method is
235 called after the initialize_telemetry() call, it will not work. The change_signal_endpoint must be used.
236 If passed in a dict in the constructor, use predefined name LOGGING_ENDPOINT_NAME. If not set,
237 the default endpoint will be used.
239 Args:
240 endpoint (str): Logging endpoint in the form '<IPv4|domain_name>:<port>'.
241 auth_token (str): Bearer auth token for the logging endpoint or None.
242 cert_ca_file (str): Absolute file path to the private cert file for logging or None. Rarely used.
244 Returns:
245 Self
247 Raises:
248 ValueError: If the endpoint format is invalid.
249 """
250 logging_endpoint = self._Endpoint(endpoint)
251 self._config[self.LOGGING_ENDPOINT_NAME] = logging_endpoint.url
252 self._endpoints[self.LOGGING_ENDPOINT_NAME] = logging_endpoint
253 if auth_token is not None:
254 self._config[self.LOGGING_AUTH_TOKEN_NAME] = auth_token
255 if cert_ca_file is not None:
256 self._config[self.LOGGING_CA_CERT_NAME] = cert_ca_file
258 return self
260 def set_tracing_endpoint(self, endpoint: str, auth_token: str = None, cert_ca_file: str = None):
261 """
262 Sets the tracing endpoint. Intended for usage prior to calling initialize_telemetry(). If this method is
263 called after the initialize_telemetry() call, it will not work. The change_signal_endpoint must be used.
264 If passed in a dict in the constructor, use predefined name TRACING_ENDPOINT_NAME. If not set,
265 the default endpoint is used.
267 Args:
268 endpoint (str): Tracing endpoint in the form '<IPv4|domain_name>:<port>'.
269 auth_token (str): Bearer auth token for the tracing endpoint or None.
270 cert_ca_file (str): Absolute file path to the private cert file for tracing or None. Rarely used.
272 Returns:
273 Self
275 Raises:
276 ValueError: If the endpoint format is invalid.
277 """
278 tracing_endpoint = self._Endpoint(endpoint)
279 self._config[self.TRACING_ENDPOINT_NAME] = tracing_endpoint.url
280 self._endpoints[self.TRACING_ENDPOINT_NAME] = tracing_endpoint
281 if auth_token is not None:
282 self._config[self.TRACING_AUTH_TOKEN_NAME] = auth_token
283 if cert_ca_file is not None:
284 self._config[self.TRACING_CA_CERT_NAME] = cert_ca_file
285 return self
287 def set_metrics_endpoint(self, endpoint: str, auth_token: str = None, cert_ca_file: str = None):
288 """
289 Sets the metrics endpoint. Intended for usage prior to calling initialize_telemetry(). If this method is
290 called after the initialize_telemetry() call, it will not work. The change_signal_endpoint must be used.
291 If passed in a dict in the constructor, use predefined name METRICS_ENDPOINT_NAME. If not set,
292 the default endpoint will be used.
294 Args:
295 endpoint (str): Metrics endpoint in the form '<IPv4|domain_name>:<port>'.
296 auth_token (str): Bearer auth token for the metrics endpoint or None.
297 cert_ca_file (str): Absolute file path to the private cert file for metrics or None. Rarely used.
299 Returns:
300 Self
302 Raises:
303 ValueError: If the endpoint format is invalid.
304 """
305 metrics_endpoint = self._Endpoint(endpoint)
306 self._config[self.METRICS_ENDPOINT_NAME] = metrics_endpoint.url
307 self._endpoints[self.METRICS_ENDPOINT_NAME] = metrics_endpoint
308 if auth_token is not None:
309 self._config[self.METRICS_AUTH_TOKEN_NAME] = auth_token
310 if cert_ca_file is not None:
311 self._config[self.METRICS_CA_CERT_NAME] = cert_ca_file
312 return self
314 def set_console_exporter(self, use_console: bool = True):
315 """
316 Sets whether to use console exporter for output. If passed in a dict in the constructor, use predefined name
317 USE_CONSOLE_EXPORTER_NAME. It applies to all exporters (logging, tracing, metrics). This is a convenience
318 used for testing only. Do not set in produiction. Also to set this value without modifying your code use the
319 environment variable 'OTEL_USE_CONSOLE_EXPORTER'. Set this to true, yes, or 1. Case doesn't matter.
321 $ export OTEL_USE_CONSOLE_EXPORTER=TRUE
323 Args:
324 use_console (bool): True to use console exporter, False otherwise.
326 Returns:
327 Self
328 """
329 self._config[self.USE_CONSOLE_EXPORTER_NAME] = use_console
330 return self
332 @deprecated
333 def set_auth_token(self, auth_token: str):
334 """
335 Sets the default authentication token for the endpoints (default endpoint). It is a fallback for all endpoints (default, logging,
336 tracing, metrics). If passed in a dict in the constructor, use predefined name
337 DEFAULT_AUTH_TOKEN_NAME.
339 Args:
340 auth_token (str): Authentication token to be used with the endpoints.
342 Returns:
343 Self
344 """
345 self._config[self.DEFAULT_AUTH_TOKEN_NAME] = auth_token
346 return self
348 @deprecated
349 def set_auth_token_logging(self, auth_token: str):
350 """
351 Sets the authentication token for the logging endpoint. If passed in a dict in the constructor, use predefined name
352 LOGGING_AUTH_TOKEN_NAME.
354 Args:
355 auth_token (str): Authentication token to be used with the endpoints.
357 Returns:
358 Self
359 """
360 self._config[self.LOGGING_AUTH_TOKEN_NAME] = auth_token
361 return self
363 @deprecated
364 def set_auth_token_tracing(self, auth_token: str):
365 """
366 Sets the authentication token for the tracing endpoint. If passed in a dict in the constructor, use predefined name
367 TRACING_AUTH_TOKEN_NAME.
369 Args:
370 auth_token (str): Authentication token to be used with the endpoints.
372 Returns:
373 Self
374 """
375 self._config[self.TRACING_AUTH_TOKEN_NAME] = auth_token
376 return self
378 @deprecated
379 def set_auth_token_metrics(self, auth_token: str):
380 """
381 Sets the authentication token for the metrics endpoint. If passed in a dict in the constructor, use predefined name
382 METRICS_AUTH_TOKEN_NAME.
384 Args:
385 auth_token (str): Authentication token to be used with the endpoints.
387 Returns:
388 Self
389 """
390 self._config[self.METRICS_AUTH_TOKEN_NAME] = auth_token
391 return self
393 @deprecated
394 def set_tls_private_ca_cert(self, cert_file: str):
395 """
396 TLS certificate used for default endpoint only.
397 Sets the actual TLS private CA certificate to be used for secure connections.
398 This is used to verify the server's certificate when using TLS. If passed in
399 a dict in the constructor, use predefined name DEFAULT_CA_CERT_NAME.
400 The caller must pass a file path that will later be utilized to find a cert.
401 Can be used to set CA to None is cert_file is None.
403 Args:
404 cert_file (str): File location of CA cert file intended for use
406 Returns:
407 Self
408 """
409 self._config[self.DEFAULT_CA_CERT_NAME] = cert_file
410 return self
412 @deprecated
413 def set_tls_private_ca_cert_logging(self, cert_file: str):
414 """
415 TLS certificate used for logging endpoint only.
416 Sets the actual TLS private CA certificate to be used for secure connections.
417 This is used to verify the server's certificate when using TLS. If passed in
418 a dict in the constructor, use predefined name LOGGING_CA_CERT_NAME.
419 The caller must pass a file path that will later be utilized to find a cert.
420 Can be used to set CA to None is cert_file is None.
422 Args:
423 cert_file (str): File location of CA cert file intended for use
425 Returns:
426 Self
427 """
428 self._config[self.LOGGING_CA_CERT_NAME] = cert_file
429 return self
431 @deprecated
432 def set_tls_private_ca_cert_tracing(self, cert_file: str):
433 """
434 TLS certificate used for tracing endpoint only.
435 Sets the actual TLS private CA certificate to be used for secure connections.
436 This is used to verify the server's certificate when using TLS. If passed in
437 a dict in the constructor, use predefined name TRACING_CA_CERT_NAME.
438 The caller must pass a file path that will later be utilized to find a cert.
439 Can be used to set CA to None is cert_file is None.
441 Args:
442 cert_file (str): File location of CA cert file intended for use
444 Returns:
445 Self
446 """
447 self._config[self.TRACING_CA_CERT_NAME] = cert_file
448 return self
450 @deprecated
451 def set_tls_private_ca_cert_metrics(self, cert_file: str):
452 """
453 TLS certificate used for metrics endpoint only.
454 Sets the actual TLS private CA certificate to be used for secure connections.
455 This is used to verify the server's certificate when using TLS. If passed in
456 a dict in the constructor, use predefined name METRICS_CA_CERT_NAME.
457 The caller must pass a file path that will later be utilized to find a cert.
458 Can be used to set CA to None is cert_file is None.
460 Args:
461 cert_file (str): File location of CA cert file intended for use
463 Returns:
464 Self
465 """
466 self._config[self.METRICS_CA_CERT_NAME] = cert_file
467 return self
469 def set_logging_level(self, level: str):
470 """
471 Sets the logging level for the telemetry logging to the collector. The built-in Python
472 logging module must be used or logging will not get sent to the server. If passed in a
473 dict in the constructor, use predefined name LOGGING_LEVEL_NAME. This will not affect
474 the logging level of the root logger, only what is sent to OTel.
476 Args:
477 level (str): Logging level to be used. It can be 'debug', 'info', 'warn', 'warning', 'error', 'fatal' or 'critical'. If not one of these strings, the logger level is not set.
479 Returns:
480 Self
481 """
482 if level not in ['debug', 'info', 'warn', 'warning', 'error', 'fatal', 'critical']:
483 return self
484 self._config[self.LOGGING_LEVEL_NAME] = level
485 return self
487 def set_metrics_export_interval_ms(self, interval_ms: int):
488 """
489 Sets the metrics export interval in milliseconds. If this value is not set,
490 the default is 60,000 milliseconds (1 minute). If passed in a dict in the constructor,
491 use predefined name METRICS_EXPORT_INTERVAL_NAME. This dictates how long the batching
492 inside OpenTelemetry lasts before sending to the collector.
494 Args:
495 interval (int): Interval in milliseconds for exporting metrics. If this is zero or
496 negative then the export interval is not set.
498 Returns:
499 Self
500 """
501 if interval_ms <= 0:
502 return self
503 self._config[self.METRICS_EXPORT_INTERVAL_MS_NAME] = interval_ms
504 return self
506 def set_tracing_export_interval_ms(self, interval_ms: int):
507 """
508 Sets the tracing export interval in milliseconds. If this value is not set,
509 the default is 60,000 milliseconds (1 minute). If passed in a dict in the constructor,
510 use predefined name TRACING_EXPORT_INTERVAL_NAME. This dictates how long the batching
511 inside OpenTelemetry lasts before sending to the collector.
513 Args:
514 interval (int): Interval in milliseconds for exporting metrics. If this is zero or
515 negative then the export interval is not set.
517 Returns:
518 Self
519 """
520 if interval_ms <= 0:
521 return self
522 self._config[self.TRACING_EXPORT_INTERVAL_MS_NAME] = interval_ms
523 return self
525 def set_tracing_session_entropy(self, session_entropy):
526 """
527 Sets the session entropy for tracing. This is used to ensure that traces are unique
528 across different sessions. If this value is not set, a default value will be used. If
529 passed in a dict in the constructor, use predefined name SESSION_ENTROPY_VALUE_NAME.
531 Args:
532 session_entropy (Any): Session entropy to be used for tracing.
534 Returns:
535 Self
536 """
537 self._config[self.SESSION_ENTROPY_VALUE_NAME] = session_entropy
538 return self
540 def set_skip_internet_check(self, value: bool):
541 """
542 Sets whether to skip the internet check. This is useful for environments that do not have
543 internet access. If passed in a dict in the constructor, use predefined name SKIP_INTERNET_CHECK_NAME.
545 Args:
546 value (bool): True to skip the internet check, False otherwise.
548 Returns:
549 Self
550 """
551 self._config[self.SKIP_INTERNET_CHECK_NAME] = value
552 return self
554 def set_use_cumulative_metrics(self, value: bool):
555 """
556 Sets the use of cumulative aggregation temporality if True. The default (False) is delta
557 (not aggregated).
559 Cumulative counters report a measurement consistently for each export interval. The would result in "duplicate"
560 metrics. To get metric readings only for the difference between the current count and the previous count, use delta
561 aggregation.
563 Args:
564 value (bool): True turns on cumulative aggregation, False (the default) is to send
565 deltas (no aggregation).
567 Returns:
568 Self
569 """
570 self._config[self.USE_CUMULATIVE_METRICS_NAME] = value
571 return self
573 def set_proxy_url(self, proxy_url: str):
574 """
575 Sets the proxy URL to use for HTTP OTLP exporters. This applies to all HTTP-based
576 signal exporters (logging, tracing, metrics). gRPC exporters are not affected.
577 If passed in a dict in the constructor, use predefined name PROXY_URL_NAME.
578 The environment variable is 'ATEL_PROXY_URL'.
580 Args:
581 proxy_url (str): The proxy URL (e.g. 'http://proxy.example.com:8080').
583 Returns:
584 Self
585 """
586 self._config[self.PROXY_URL_NAME] = proxy_url
587 return self
589 def set_shutdown_on_exit(self, value: bool):
590 """
591 Sets whether providers register atexit handlers that flush on interpreter exit.
592 If True (default), each provider registers an ``atexit`` handler that flushes on interpreter exit.
593 If False, no atexit handlers are registered and the caller becomes responsible for flushing:
594 call ``shutdown_telemetry()`` (or ``flush_telemetry()``) before the process exits, otherwise
595 buffered telemetry is silently dropped.The environment variable is 'ATEL_SHUTDOWN_ON_EXIT'.
597 Args:
598 value (bool): True to register atexit handlers, False to manage shutdown manually.
600 Returns:
601 Self
602 """
603 self._config[self.SHUTDOWN_ON_EXIT_NAME] = value
604 return self
606 def set_verbose_export_errors(self, value: bool):
607 """
608 Sets whether OpenTelemetry export errors are logged to stdout/stderr.
609 If False (default), export errors from the OpenTelemetry SDK are suppressed.
610 If True, export errors such as "Transient error" will be logged by the OpenTelemetry SDK.
611 The environment variable is 'ATEL_VERBOSE_EXPORT_ERRORS'. This will bubble up to global
612 OpenTelemetry logging, so make sure to set this to True off if those logs are important, or
613 in development.
615 Args:
616 value (bool): True to show export errors, False to suppress them.
618 Returns:
619 Self
620 """
621 self._config[self.VERBOSE_EXPORT_ERRORS_NAME] = value
622 return self
624 def set_disable_session_id(self, value: bool):
625 """
626 Sets whether to disable automatic session ID generation.
627 If False (default), a session ID is automatically generated and added to telemetry.
628 If True, no session ID will be generated or added to telemetry resource attributes.
629 The environment variable is 'ATEL_DISABLE_SESSION_ID'.
631 Args:
632 value (bool): True to disable session ID generation, False to enable it.
634 Returns:
635 Self
636 """
637 self._config[self.DISABLE_SESSION_ID_NAME] = value
638 return self
640 def _get_proxy_url(self) -> str:
641 return self._config.get(self.PROXY_URL_NAME, None)
643 def _create_proxy_session(self):
644 proxy_url = self._get_proxy_url()
645 if proxy_url is None:
646 return None
647 import requests
648 session = requests.Session()
649 session.proxies = {
650 'http': proxy_url,
651 'https': proxy_url
652 }
653 return session
656 class _Endpoint:
657 def __init__(self, endpoint: str):
658 # Properties:
659 # - protocol - protocol of the endpoint passed to the constructor
660 # - host - host of the endpoint passed to the constructor
661 # - port - port of the endpoint passed to the constructor
662 # - path - path of the endpoint passed to the constructor
663 # - valid - whether or not the endpoint is valid
664 # - _internet_check_port - internet check port used for connection check
665 self._parse_endpoint(endpoint.strip())
667 # Getters for configuration settings (internal only for the package)
668 def _parse_endpoint(self, url: str):
669 self._validate_endpoint(url)
671 # Default port for internet check
672 if self.port is None:
673 if self.protocol == 'http':
674 self._internet_check_port = 80
675 else:
676 # HTTPS and gRPC(s) use 443 by default
677 self._internet_check_port = 443
679 # allow default port usage from user specification
680 elif self.port not in (80, 443) and not (1024 <= self.port <= 65535):
681 raise ValueError(f"Invalid endpoint format: {url}")
682 # Internet check port is user port if one is specified and valid
683 else:
684 self._internet_check_port = self.port
686 # prepare whole url
687 url = f"{self.protocol}://{self.host}"
688 if self.port:
689 url += f":{self.port}"
690 url += self.path
692 self.url = url
694 def _validate_endpoint(self, endpoint: str):
695 if endpoint == '':
696 raise ValueError(f"Invalid endpoint format: {endpoint}")
697 pattern = re.compile(
698 r"^"
699 r"(https?://|grpcs?://)" # capture group 1: optional protocol
700 r"(" # capture group 2: host
701 r"(?!0\.)" # Disallow IPs starting with 0.
702 r"(?:\d{1,3}\.){3}\d{1,3}" # IPv4 format (non-capturing group)
703 r"|"
704 r"(?:[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*" # domain segment
705 r"(?:\.[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)*)" # more segments
706 r")"
707 r"(?::(\d{1,5}))?" # capture group 3: optional port
708 r"(/.*)?$" # capture group 4: optional path
709 )
711 match = pattern.match(endpoint)
712 if not match:
713 raise ValueError(f"Invalid endpoint format: {endpoint}")
715 protocol_str = match.group(1)
716 self.host = match.group(2)
717 port = match.group(3)
718 self.port = int(port) if port is not None else None
719 self.path = match.group(4) or ""
721 # Extract protocol
722 self.protocol = protocol_str.rstrip('://')
723 # Determine tls
724 self.tls = True if self.protocol[-1] == 's' else False
726 # If it's an IP, validate each octet
727 if re.match(r"^(\d{1,3}\.)+\d{1,3}$", self.host):
728 quads = list(map(int, self.host.split('.')))
729 if len(quads) != 4:
730 raise ValueError(f"Invalid endpoint format: {endpoint}")
731 if quads[0] == 0 or quads[0] == 255 or quads[3] == 0 or quads[3] == 255:
732 raise ValueError(f"Invalid endpoint format: {endpoint}")
733 for q in quads:
734 if q > 255:
735 raise ValueError(f"Invalid endpoint format: {endpoint}")
737 def _change_signal_endpoint(self, signal: str, new_endpoint: str, auth_token: str=None):
738 set_endpoint = getattr(self, f"set_{signal}_endpoint", None)
739 set_endpoint(new_endpoint, auth_token=auth_token)
740 get_endpoint = getattr(self, f"_get_{signal}_endpoint", None)
741 return get_endpoint()
743 def _set_otel_signal_endpoint(self, endpoint: str, signal: str) -> str:
744 if endpoint.lower().startswith("grpc"):
745 return endpoint
746 endpoint_str = f"v1/{signal}"
747 if not endpoint.endswith(endpoint_str):
748 endpoint_str = "/" + endpoint_str if endpoint[-1] != "/" else endpoint_str
749 return endpoint + endpoint_str
750 else:
751 return endpoint
753 def _get_default_endpoint(self) -> str:
754 return self._config.get(self.DEFAULT_ENDPOINT_NAME, '')
756 def _get_logging_endpoint(self) -> str:
757 endpoint = self._config.get(self.LOGGING_ENDPOINT_NAME, self._get_default_endpoint())
758 return self._set_otel_signal_endpoint(endpoint, "logs")
760 def _get_tracing_endpoint(self) -> str:
761 endpoint = self._config.get(self.TRACING_ENDPOINT_NAME, self._get_default_endpoint())
762 return self._set_otel_signal_endpoint(endpoint, "traces")
764 def _get_metrics_endpoint(self) -> str:
765 endpoint = self._config.get(self.METRICS_ENDPOINT_NAME, self._get_default_endpoint())
766 return self._set_otel_signal_endpoint(endpoint, "metrics")
768 def _prepare_ca_cert(self, protocol: str, cert_file: str) -> str:
769 if protocol in ['http', 'https']:
770 return cert_file # just return cert file for HTTP exporter ca_cert
771 else:
772 if cert_file:
773 with open(cert_file, 'rb') as f:
774 ca_cert_bytes = f.read() # gRPC exporter requires a bytes string
775 creds = grpc.ssl_channel_credentials(root_certificates=ca_cert_bytes)
776 return creds
777 else:
778 # Use system trust store (for public CAs)
779 creds = grpc.ssl_channel_credentials()
780 return creds
782 def _get_ca_cert_default(self) -> str:
783 cert_file = self._config.get(self.DEFAULT_CA_CERT_NAME, None)
784 return self._prepare_ca_cert(self._get_request_protocol_default().protocol, cert_file)
786 def _get_ca_cert_logging(self) -> str:
787 cert_file = self._config.get(self.LOGGING_CA_CERT_NAME, self._get_ca_cert_default())
788 return self._prepare_ca_cert(self._get_request_protocol_logging(), cert_file)
790 def _get_ca_cert_tracing(self) -> str:
791 cert_file = self._config.get(self.TRACING_CA_CERT_NAME, self._get_ca_cert_default())
792 return self._prepare_ca_cert(self._get_request_protocol_tracing(), cert_file)
794 def _get_ca_cert_metrics(self) -> str:
795 cert_file = self._config.get(self.METRICS_CA_CERT_NAME, self._get_ca_cert_default())
796 return self._prepare_ca_cert(self._get_request_protocol_metrics(), cert_file)
798 def _get_console_exporter(self) -> bool:
799 return self._config.get(self.USE_CONSOLE_EXPORTER_NAME, False)
801 def _get_auth_token_default(self) -> str:
802 return self._config.get(self.DEFAULT_AUTH_TOKEN_NAME, None)
804 def _get_auth_token_logging(self) -> str:
805 return self._config.get(self.LOGGING_AUTH_TOKEN_NAME, self._get_auth_token_default())
807 def _get_auth_token_tracing(self) -> str:
808 return self._config.get(self.TRACING_AUTH_TOKEN_NAME, self._get_auth_token_default())
810 def _get_auth_token_metrics(self) -> str:
811 return self._config.get(self.METRICS_AUTH_TOKEN_NAME, self._get_auth_token_default())
813 def _get_logging_level(self) -> str:
814 return self._config.get(self.LOGGING_LEVEL_NAME, 'warning')
816 def _get_metrics_export_interval_ms(self) -> int:
817 return self._config.get(self.METRICS_EXPORT_INTERVAL_MS_NAME, 60_000)
819 def _get_tracing_export_interval_ms(self) -> int:
820 return self._config.get(self.TRACING_EXPORT_INTERVAL_MS_NAME, 60_000)
822 def _get_tracing_session_entropy(self):
823 if self._config.get(self.SESSION_ENTROPY_VALUE_NAME, None) is None:
824 import time
825 self._config[self.SESSION_ENTROPY_VALUE_NAME] = int(time.time() * 1e9)
826 return self._config.get(self.SESSION_ENTROPY_VALUE_NAME)
828 def _get_skip_internet_check(self) -> bool:
829 return self._config.get(self.SKIP_INTERNET_CHECK_NAME, False)
831 def _get_TLS_default(self) -> bool:
832 # will raise if there is no default tls (means there is no default endpoint)
833 return self._endpoints[self.DEFAULT_ENDPOINT_NAME]
835 def _get_TLS_logging(self) -> str:
836 return self._endpoints.get(self.LOGGING_ENDPOINT_NAME, self._get_TLS_default()).tls
838 def _get_TLS_metrics(self) -> str:
839 return self._endpoints.get(self.METRICS_ENDPOINT_NAME, self._get_TLS_default()).tls
841 def _get_TLS_tracing(self) -> str:
842 return self._endpoints.get(self.TRACING_ENDPOINT_NAME, self._get_TLS_default()).tls
844 def _get_request_protocol_default(self) -> str:
845 # will raise if there is no default protocol (means there is no default endpoint)
846 return self._endpoints[self.DEFAULT_ENDPOINT_NAME]
848 def _get_request_protocol_logging(self) -> str:
849 return self._endpoints.get(self.LOGGING_ENDPOINT_NAME, self._get_request_protocol_default()).protocol
851 def _get_request_protocol_metrics(self) -> str:
852 return self._endpoints.get(self.METRICS_ENDPOINT_NAME, self._get_request_protocol_default()).protocol
854 def _get_request_protocol_tracing(self) -> str:
855 return self._endpoints.get(self.TRACING_ENDPOINT_NAME, self._get_request_protocol_default()).protocol
857 def _get_use_cumulative_metrics(self) -> bool:
858 return self._config.get(self.USE_CUMULATIVE_METRICS_NAME, False)
860 def _get_shutdown_on_exit(self) -> bool:
861 return self._config.get(self.SHUTDOWN_ON_EXIT_NAME, True)
863 def _get_verbose_export_errors(self) -> bool:
864 return self._config.get(self.VERBOSE_EXPORT_ERRORS_NAME, False)
866 def _get_disable_session_id(self) -> bool:
867 return self._config.get(self.DISABLE_SESSION_ID_NAME, False)