valkey-glide 2.0.0rc7__cp39-cp39-macosx_11_0_arm64.whl → 2.0.1__cp39-cp39-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of valkey-glide might be problematic. Click here for more details.

@@ -61,6 +61,7 @@ from glide.constants import (
61
61
  TXInfoStreamFullResponse,
62
62
  TXInfoStreamResponse,
63
63
  )
64
+ from glide.exceptions import RequestError
64
65
  from glide.protobuf.command_request_pb2 import RequestType
65
66
  from glide.routes import Route
66
67
 
@@ -2535,8 +2536,8 @@ class CoreCommands(Protocol):
2535
2536
  Returns:
2536
2537
  TOK: A simple "OK" response.
2537
2538
 
2538
- If `start` exceeds the end of the list, or if `start` is greater than `end`, the result will be an empty list
2539
- (which causes `key` to be removed).
2539
+ If `start` exceeds the end of the list, or if `start` is greater than `end`, the list is emptied
2540
+ and the key is removed.
2540
2541
 
2541
2542
  If `end` exceeds the actual end of the list, it will be treated like the last element of the list.
2542
2543
 
@@ -2554,9 +2555,6 @@ class CoreCommands(Protocol):
2554
2555
  async def lrem(self, key: TEncodable, count: int, element: TEncodable) -> int:
2555
2556
  """
2556
2557
  Removes the first `count` occurrences of elements equal to `element` from the list stored at `key`.
2557
- If `count` is positive, it removes elements equal to `element` moving from head to tail.
2558
- If `count` is negative, it removes elements equal to `element` moving from tail to head.
2559
- If `count` is 0 or greater than the occurrences of elements equal to `element`, it removes all elements
2560
2558
  equal to `element`.
2561
2559
 
2562
2560
  See [valkey.io](https://valkey.io/commands/lrem/) for more details.
@@ -2564,6 +2562,11 @@ class CoreCommands(Protocol):
2564
2562
  Args:
2565
2563
  key (TEncodable): The key of the list.
2566
2564
  count (int): The count of occurrences of elements equal to `element` to remove.
2565
+
2566
+ - If `count` is positive, it removes elements equal to `element` moving from head to tail.
2567
+ - If `count` is negative, it removes elements equal to `element` moving from tail to head.
2568
+ - If `count` is 0 or greater than the occurrences of elements equal to `element`, it removes all elements
2569
+
2567
2570
  element (TEncodable): The element to remove from the list.
2568
2571
 
2569
2572
  Returns:
@@ -6048,19 +6051,19 @@ class CoreCommands(Protocol):
6048
6051
  elements (List[TEncodable]): A list of members to add to the HyperLogLog stored at `key`.
6049
6052
 
6050
6053
  Returns:
6051
- int: If the HyperLogLog is newly created, or if the HyperLogLog approximated cardinality is
6052
- altered, then returns 1.
6054
+ bool: If the HyperLogLog is newly created, or if the HyperLogLog approximated cardinality is
6055
+ altered, then returns `True`.
6053
6056
 
6054
- Otherwise, returns 0.
6057
+ Otherwise, returns `False`.
6055
6058
 
6056
6059
  Examples:
6057
6060
  >>> await client.pfadd("hll_1", ["a", "b", "c" ])
6058
- 1 # A data structure was created or modified
6061
+ True # A data structure was created or modified
6059
6062
  >>> await client.pfadd("hll_2", [])
6060
- 1 # A new empty data structure was created
6063
+ True # A new empty data structure was created
6061
6064
  """
6062
6065
  return cast(
6063
- int,
6066
+ bool,
6064
6067
  await self._execute_command(RequestType.PfAdd, [key] + elements),
6065
6068
  )
6066
6069
 
@@ -6649,6 +6652,10 @@ class CoreCommands(Protocol):
6649
6652
  args.append("REPLACE")
6650
6653
  if absttl is True:
6651
6654
  args.append("ABSTTL")
6655
+ if idletime is not None and frequency is not None:
6656
+ raise RequestError(
6657
+ "syntax error: IDLETIME and FREQ cannot be set at the same time."
6658
+ )
6652
6659
  if idletime is not None:
6653
6660
  args.extend(["IDLETIME", str(idletime)])
6654
6661
  if frequency is not None:
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from typing import Dict, List, Mapping, Optional, Union, cast
6
6
 
7
7
  from glide.async_commands.batch import Batch
8
+ from glide.async_commands.batch_options import BatchOptions
8
9
  from glide.async_commands.command_args import ObjectType
9
10
  from glide.async_commands.core import (
10
11
  CoreCommands,
@@ -76,7 +77,7 @@ class StandaloneCommands(CoreCommands):
76
77
  self,
77
78
  batch: Batch,
78
79
  raise_on_error: bool,
79
- timeout: Optional[int] = None,
80
+ options: Optional[BatchOptions] = None,
80
81
  ) -> Optional[List[TResult]]:
81
82
  """
82
83
  Executes a batch by processing the queued commands.
@@ -96,10 +97,7 @@ class StandaloneCommands(CoreCommands):
96
97
  When set to ``False``, errors will be included as part of the batch response array, allowing
97
98
  the caller to process both successful and failed commands together. In this case, error details
98
99
  will be provided as instances of ``RequestError``.
99
- timeout (Optional[int]): The duration in milliseconds that the client should wait for the batch request
100
- to complete. This duration encompasses sending the request, awaiting a response from the server, and any
101
- required reconnections or retries. If the specified timeout is exceeded for the request,
102
- a timeout error will be raised. If not explicitly set, the client's default request timeout will be used.
100
+ options (Optional[BatchOptions]): A ``BatchOptions`` object containing execution options.
103
101
 
104
102
  Returns:
105
103
  Optional[List[TResult]]: An array of results, where each entry corresponds to a command's execution result.
@@ -125,33 +123,38 @@ class StandaloneCommands(CoreCommands):
125
123
  # Expected Output: Pipeline Batch Result: [OK, OK, b'value1', b'value2']
126
124
 
127
125
  Example (Atomic Batch - Transaction with options):
126
+ >>> from glide import BatchOptions
128
127
  >>> transaction = Batch(is_atomic=True)
129
128
  >>> transaction.set("key", "1")
130
129
  >>> transaction.incr("key")
131
130
  >>> transaction.custom_command(["get", "key"])
131
+ >>> options = BatchOptions(timeout=1000) # Set a timeout of 1000 milliseconds
132
132
  >>> result = await client.exec(
133
133
  ... transaction,
134
134
  ... raise_on_error=False, # Do not raise an error on failure
135
- ... timeout=1000 # Set a timeout of 1000 milliseconds
135
+ ... options=options
136
136
  ... )
137
137
  >>> print(f"Transaction Result: {result}")
138
138
  # Expected Output: Transaction Result: [OK, 2, b'2']
139
139
 
140
140
  Example (Non-Atomic Batch - Pipeline with options):
141
+ >>> from glide import BatchOptions
141
142
  >>> pipeline = Batch(is_atomic=False)
142
143
  >>> pipeline.custom_command(["set", "key1", "value1"])
143
144
  >>> pipeline.custom_command(["set", "key2", "value2"])
144
145
  >>> pipeline.custom_command(["get", "key1"])
145
146
  >>> pipeline.custom_command(["get", "key2"])
147
+ >>> options = BatchOptions(timeout=1000) # Set a timeout of 1000 milliseconds
146
148
  >>> result = await client.exec(
147
149
  ... pipeline,
148
150
  ... raise_on_error=False, # Do not raise an error on failure
149
- ... timeout=1000 # Set a timeout of 1000 milliseconds
151
+ ... options=options
150
152
  ... )
151
153
  >>> print(f"Pipeline Result: {result}")
152
154
  # Expected Output: Pipeline Result: [OK, OK, b'value1', b'value2']
153
155
  """
154
156
  commands = batch.commands[:]
157
+ timeout = options.timeout if options else None
155
158
  return await self._execute_batch(
156
159
  commands,
157
160
  is_atomic=batch.is_atomic,
glide/config.py CHANGED
@@ -73,7 +73,7 @@ class BackoffStrategy:
73
73
  """
74
74
  Represents the strategy used to determine how and when to reconnect, in case of connection failures.
75
75
  The time between attempts grows exponentially, to the formula rand(0 .. factor * (exponentBase ^ N)), where N
76
- is the number of failed attempts.
76
+ is the number of failed attempts, and `rand(...)` applies a jitter of up to `jitter_percent`% to introduce randomness and reduce retry storms.
77
77
  Once the maximum value is reached, that will remain the time between retry attempts until a reconnect attempt is
78
78
  successful.
79
79
  The client will attempt to reconnect indefinitely.
@@ -147,25 +147,66 @@ class PeriodicChecksStatus(Enum):
147
147
  """
148
148
 
149
149
 
150
+ class TlsAdvancedConfiguration:
151
+ """
152
+ Represents advanced TLS configuration settings.
153
+
154
+ Attributes:
155
+ use_insecure_tls (Optional[bool]): Whether to bypass TLS certificate verification.
156
+
157
+ - When set to True, the client skips certificate validation.
158
+ This is useful when connecting to servers or clusters using self-signed certificates,
159
+ or when DNS entries (e.g., CNAMEs) don't match certificate hostnames.
160
+
161
+ - This setting is typically used in development or testing environments. **It is
162
+ strongly discouraged in production**, as it introduces security risks such as man-in-the-middle attacks.
163
+
164
+ - Only valid if TLS is already enabled in the base client configuration.
165
+ Enabling it without TLS will result in a `ConfigurationError`.
166
+
167
+ - Default: False (verification is enforced).
168
+ """
169
+
170
+ def __init__(self, use_insecure_tls: Optional[bool] = None):
171
+ self.use_insecure_tls = use_insecure_tls
172
+
173
+
150
174
  class AdvancedBaseClientConfiguration:
151
175
  """
152
176
  Represents the advanced configuration settings for a base Glide client.
153
177
 
154
178
  Attributes:
155
179
  connection_timeout (Optional[int]): The duration in milliseconds to wait for a TCP/TLS connection to complete.
156
- This applies both during initial client creation and any reconnections that may occur during request processing.
180
+ This applies both during initial client creation and any reconnection that may occur during request processing.
157
181
  **Note**: A high connection timeout may lead to prolonged blocking of the entire command pipeline.
158
- If not explicitly set, a default value of 250 milliseconds will be used.
182
+ If not explicitly set, a default value of 2000 milliseconds will be used.
183
+ tls_config (Optional[TlsAdvancedConfiguration]): The advanced TLS configuration settings.
184
+ This allows for more granular control of TLS behavior, such as enabling an insecure mode
185
+ that bypasses certificate validation.
159
186
  """
160
187
 
161
- def __init__(self, connection_timeout: Optional[int] = None):
188
+ def __init__(
189
+ self,
190
+ connection_timeout: Optional[int] = None,
191
+ tls_config: Optional[TlsAdvancedConfiguration] = None,
192
+ ):
162
193
  self.connection_timeout = connection_timeout
194
+ self.tls_config = tls_config
163
195
 
164
196
  def _create_a_protobuf_conn_request(
165
197
  self, request: ConnectionRequest
166
198
  ) -> ConnectionRequest:
167
199
  if self.connection_timeout:
168
200
  request.connection_timeout = self.connection_timeout
201
+
202
+ if self.tls_config and self.tls_config.use_insecure_tls:
203
+ if request.tls_mode == TlsMode.SecureTls:
204
+ request.tls_mode = TlsMode.InsecureTls
205
+ elif request.tls_mode == TlsMode.NoTls:
206
+ raise ConfigurationError(
207
+ "use_insecure_tls cannot be enabled when use_tls is disabled."
208
+ )
209
+
169
210
  return request
170
211
 
171
212
 
@@ -187,14 +228,15 @@ class BaseClientConfiguration:
187
228
  ].
188
229
 
189
230
  use_tls (bool): True if communication with the cluster should use Transport Level Security.
190
- Should match the TLS configuration of the server/cluster, otherwise the connection attempt will fail
231
+ Should match the TLS configuration of the server/cluster, otherwise the connection attempt will fail.
232
+ For advanced tls configuration, please use `AdvancedBaseClientConfiguration`.
191
233
  credentials (ServerCredentials): Credentials for authentication process.
192
234
  If none are set, the client will not authenticate itself with the server.
193
235
  read_from (ReadFrom): If not set, `PRIMARY` will be used.
194
236
  request_timeout (Optional[int]): The duration in milliseconds that the client should wait for a request to
195
237
  complete.
196
238
  This duration encompasses sending the request, awaiting for a response from the server, and any required
197
- reconnections or retries.
239
+ reconnection or retries.
198
240
  If the specified timeout is exceeded for a pending request, it will result in a timeout error. If not
199
241
  explicitly set, a default value of 250 milliseconds will be used.
200
242
  reconnect_strategy (Optional[BackoffStrategy]): Strategy used to determine how and when to reconnect, in case of
@@ -214,6 +256,25 @@ class BaseClientConfiguration:
214
256
  If ReadFrom strategy is AZAffinityReplicasAndPrimary, this setting ensures that readonly commands are directed
215
257
  to nodes (first replicas then primary) within the specified AZ if they exist.
216
258
  advanced_config (Optional[AdvancedBaseClientConfiguration]): Advanced configuration settings for the client.
259
+
260
+ lazy_connect (Optional[bool]): Enables lazy connection mode, where physical connections to the server(s)
261
+ are deferred until the first command is sent. This can reduce startup latency and allow for client
262
+ creation in disconnected environments.
263
+
264
+ When set to `True`, the client will not attempt to connect to the specified nodes during
265
+ initialization. Instead, connections will be established only when a command is actually executed.
266
+
267
+ Note that the first command executed with lazy connections may experience additional latency
268
+ as it needs to establish the connection first. During this initial connection, the standard
269
+ request timeout does not apply yet - instead, the connection establishment is governed by
270
+ `AdvancedBaseClientConfiguration.connection_timeout`. The request timeout (`request_timeout`)
271
+ only begins counting after the connection has been successfully established. This behavior
272
+ can effectively increase the total time needed for the first command to complete.
273
+
274
+ This setting applies to both standalone and cluster modes. Note that if an operation is
275
+ attempted and connection fails (e.g., unreachable nodes), errors will surface at that point.
276
+
277
+ If not set, connections are established immediately during client creation (equivalent to `False`).
217
278
  """
218
279
 
219
280
  def __init__(
@@ -229,6 +290,7 @@ class BaseClientConfiguration:
229
290
  inflight_requests_limit: Optional[int] = None,
230
291
  client_az: Optional[str] = None,
231
292
  advanced_config: Optional[AdvancedBaseClientConfiguration] = None,
293
+ lazy_connect: Optional[bool] = None,
232
294
  ):
233
295
  self.addresses = addresses
234
296
  self.use_tls = use_tls
@@ -241,6 +303,7 @@ class BaseClientConfiguration:
241
303
  self.inflight_requests_limit = inflight_requests_limit
242
304
  self.client_az = client_az
243
305
  self.advanced_config = advanced_config
306
+ self.lazy_connect = lazy_connect
244
307
 
245
308
  if read_from == ReadFrom.AZ_AFFINITY and not client_az:
246
309
  raise ValueError(
@@ -301,6 +364,8 @@ class BaseClientConfiguration:
301
364
  if self.advanced_config:
302
365
  self.advanced_config._create_a_protobuf_conn_request(request)
303
366
 
367
+ if self.lazy_connect is not None:
368
+ request.lazy_connect = self.lazy_connect
304
369
  return request
305
370
 
306
371
  def _is_pubsub_configured(self) -> bool:
@@ -317,9 +382,13 @@ class AdvancedGlideClientConfiguration(AdvancedBaseClientConfiguration):
317
382
  Represents the advanced configuration settings for a Standalone Glide client.
318
383
  """
319
384
 
320
- def __init__(self, connection_timeout: Optional[int] = None):
385
+ def __init__(
386
+ self,
387
+ connection_timeout: Optional[int] = None,
388
+ tls_config: Optional[TlsAdvancedConfiguration] = None,
389
+ ):
321
390
 
322
- super().__init__(connection_timeout)
391
+ super().__init__(connection_timeout, tls_config)
323
392
 
324
393
 
325
394
  class GlideClientConfiguration(BaseClientConfiguration):
@@ -337,12 +406,13 @@ class GlideClientConfiguration(BaseClientConfiguration):
337
406
  ]
338
407
 
339
408
  use_tls (bool): True if communication with the cluster should use Transport Level Security.
409
+ Please use `AdvancedGlideClusterClientConfiguration`.
340
410
  credentials (ServerCredentials): Credentials for authentication process.
341
411
  If none are set, the client will not authenticate itself with the server.
342
412
  read_from (ReadFrom): If not set, `PRIMARY` will be used.
343
413
  request_timeout (Optional[int]): The duration in milliseconds that the client should wait for a request to complete.
344
414
  This duration encompasses sending the request, awaiting for a response from the server, and any required
345
- reconnections or retries.
415
+ reconnection or retries.
346
416
  If the specified timeout is exceeded for a pending request, it will result in a timeout error.
347
417
  If not explicitly set, a default value of 250 milliseconds will be used.
348
418
  reconnect_strategy (Optional[BackoffStrategy]): Strategy used to determine how and when to reconnect, in case of
@@ -414,6 +484,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
414
484
  inflight_requests_limit: Optional[int] = None,
415
485
  client_az: Optional[str] = None,
416
486
  advanced_config: Optional[AdvancedGlideClientConfiguration] = None,
487
+ lazy_connect: Optional[bool] = None,
417
488
  ):
418
489
  super().__init__(
419
490
  addresses=addresses,
@@ -427,6 +498,7 @@ class GlideClientConfiguration(BaseClientConfiguration):
427
498
  inflight_requests_limit=inflight_requests_limit,
428
499
  client_az=client_az,
429
500
  advanced_config=advanced_config,
501
+ lazy_connect=lazy_connect,
430
502
  )
431
503
  self.database_id = database_id
432
504
  self.pubsub_subscriptions = pubsub_subscriptions
@@ -479,8 +551,12 @@ class AdvancedGlideClusterClientConfiguration(AdvancedBaseClientConfiguration):
479
551
  Represents the advanced configuration settings for a Glide Cluster client.
480
552
  """
481
553
 
482
- def __init__(self, connection_timeout: Optional[int] = None):
483
- super().__init__(connection_timeout)
554
+ def __init__(
555
+ self,
556
+ connection_timeout: Optional[int] = None,
557
+ tls_config: Optional[TlsAdvancedConfiguration] = None,
558
+ ):
559
+ super().__init__(connection_timeout, tls_config)
484
560
 
485
561
 
486
562
  class GlideClusterClientConfiguration(BaseClientConfiguration):
@@ -497,12 +573,13 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
497
573
  ]
498
574
 
499
575
  use_tls (bool): True if communication with the cluster should use Transport Level Security.
576
+ For advanced tls configuration, please use `AdvancedGlideClusterClientConfiguration`.
500
577
  credentials (ServerCredentials): Credentials for authentication process.
501
578
  If none are set, the client will not authenticate itself with the server.
502
579
  read_from (ReadFrom): If not set, `PRIMARY` will be used.
503
580
  request_timeout (Optional[int]): The duration in milliseconds that the client should wait for a request to complete.
504
581
  This duration encompasses sending the request, awaiting for a response from the server, and any required
505
- reconnections or retries.
582
+ reconnection or retries.
506
583
  If the specified timeout is exceeded for a pending request, it will result in a timeout error. If not explicitly
507
584
  set, a default value of 250 milliseconds will be used.
508
585
  reconnect_strategy (Optional[BackoffStrategy]): Strategy used to determine how and when to reconnect, in case of
@@ -586,6 +663,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
586
663
  inflight_requests_limit: Optional[int] = None,
587
664
  client_az: Optional[str] = None,
588
665
  advanced_config: Optional[AdvancedGlideClusterClientConfiguration] = None,
666
+ lazy_connect: Optional[bool] = None,
589
667
  ):
590
668
  super().__init__(
591
669
  addresses=addresses,
@@ -599,6 +677,7 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
599
677
  inflight_requests_limit=inflight_requests_limit,
600
678
  client_az=client_az,
601
679
  advanced_config=advanced_config,
680
+ lazy_connect=lazy_connect,
602
681
  )
603
682
  self.periodic_checks = periodic_checks
604
683
  self.pubsub_subscriptions = pubsub_subscriptions
@@ -637,6 +716,8 @@ class GlideClusterClientConfiguration(BaseClientConfiguration):
637
716
  for channel_pattern in channels_patterns:
638
717
  entry.channels_or_patterns.append(str.encode(channel_pattern))
639
718
 
719
+ if self.lazy_connect is not None:
720
+ request.lazy_connect = self.lazy_connect
640
721
  return request
641
722
 
642
723
  def _is_pubsub_configured(self) -> bool:
Binary file
glide/glide.pyi CHANGED
@@ -27,6 +27,28 @@ class ClusterScanCursor:
27
27
  def get_cursor(self) -> str: ...
28
28
  def is_finished(self) -> bool: ...
29
29
 
30
+ class OpenTelemetryConfig:
31
+ def __init__(
32
+ self,
33
+ traces: Optional[OpenTelemetryTracesConfig] = None,
34
+ metrics: Optional[OpenTelemetryMetricsConfig] = None,
35
+ flush_interval_ms: Optional[int] = None,
36
+ ) -> None: ...
37
+ def get_traces(self) -> Optional[OpenTelemetryTracesConfig]: ...
38
+ def set_traces(self, traces: OpenTelemetryTracesConfig) -> None: ...
39
+ def get_metrics(self) -> Optional[OpenTelemetryMetricsConfig]: ...
40
+
41
+ class OpenTelemetryTracesConfig:
42
+ def __init__(
43
+ self, endpoint: str, sample_percentage: Optional[int] = None
44
+ ) -> None: ...
45
+ def get_endpoint(self) -> str: ...
46
+ def get_sample_percentage(self) -> Optional[int]: ...
47
+
48
+ class OpenTelemetryMetricsConfig:
49
+ def __init__(self, endpoint: str) -> None: ...
50
+ def get_endpoint(self) -> str: ...
51
+
30
52
  def start_socket_listener_external(init_callback: Callable) -> None: ...
31
53
  def value_from_pointer(pointer: int) -> TResult: ...
32
54
  def create_leaked_value(message: str) -> int: ...
@@ -34,3 +56,6 @@ def create_leaked_bytes_vec(args_vec: List[bytes]) -> int: ...
34
56
  def get_statistics() -> dict: ...
35
57
  def py_init(level: Optional[Level], file_name: Optional[str]) -> Level: ...
36
58
  def py_log(log_level: Level, log_identifier: str, message: str) -> None: ...
59
+ def create_otel_span(name: str) -> int: ...
60
+ def drop_otel_span(span_ptr: int) -> None: ...
61
+ def init_opentelemetry(config: OpenTelemetryConfig) -> None: ...
glide/glide_client.py CHANGED
@@ -36,6 +36,7 @@ from glide.exceptions import (
36
36
  )
37
37
  from glide.logger import Level as LogLevel
38
38
  from glide.logger import Logger as ClientLogger
39
+ from glide.opentelemetry import OpenTelemetry
39
40
  from glide.protobuf.command_request_pb2 import Command, CommandRequest, RequestType
40
41
  from glide.protobuf.connection_request_pb2 import ConnectionRequest
41
42
  from glide.protobuf.response_pb2 import RequestErrorType, Response
@@ -47,6 +48,8 @@ from .glide import (
47
48
  MAX_REQUEST_ARGS_LEN,
48
49
  ClusterScanCursor,
49
50
  create_leaked_bytes_vec,
51
+ create_otel_span,
52
+ drop_otel_span,
50
53
  get_statistics,
51
54
  start_socket_listener_external,
52
55
  value_from_pointer,
@@ -410,6 +413,13 @@ class BaseClient(CoreCommands):
410
413
  raise ClosingError(
411
414
  "Unable to execute requests; the client is closed. Please create a new client."
412
415
  )
416
+
417
+ # Create span if OpenTelemetry is configured and sampling indicates we should trace
418
+ span = None
419
+ if OpenTelemetry.should_sample():
420
+ command_name = RequestType.Name(request_type)
421
+ span = create_otel_span(command_name)
422
+
413
423
  request = CommandRequest()
414
424
  request.callback_idx = self._get_callback_index()
415
425
  request.single_command.request_type = request_type
@@ -424,6 +434,11 @@ class BaseClient(CoreCommands):
424
434
  request.single_command.args_vec_pointer = create_leaked_bytes_vec(
425
435
  encoded_args
426
436
  )
437
+
438
+ # Add span pointer to request if span was created
439
+ if span:
440
+ request.root_span_ptr = span
441
+
427
442
  set_protobuf_route(request, route)
428
443
  return await self._write_request_await_response(request)
429
444
 
@@ -441,6 +456,14 @@ class BaseClient(CoreCommands):
441
456
  raise ClosingError(
442
457
  "Unable to execute requests; the client is closed. Please create a new client."
443
458
  )
459
+
460
+ # Create span if OpenTelemetry is configured and sampling indicates we should trace
461
+ span = None
462
+
463
+ if OpenTelemetry.should_sample():
464
+ # Use "Batch" as span name for batches
465
+ span = create_otel_span("Batch")
466
+
444
467
  request = CommandRequest()
445
468
  request.callback_idx = self._get_callback_index()
446
469
  batch_commands = []
@@ -462,6 +485,11 @@ class BaseClient(CoreCommands):
462
485
  request.batch.timeout = timeout
463
486
  request.batch.retry_server_error = retry_server_error
464
487
  request.batch.retry_connection_error = retry_connection_error
488
+
489
+ # Add span pointer to request if span was created
490
+ if span:
491
+ request.root_span_ptr = span
492
+
465
493
  set_protobuf_route(request, route)
466
494
  return await self._write_request_await_response(request)
467
495
 
@@ -656,6 +684,10 @@ class BaseClient(CoreCommands):
656
684
  else:
657
685
  res_future.set_result(None)
658
686
 
687
+ # Clean up span if it was created
688
+ if response.HasField("root_span_ptr"):
689
+ drop_otel_span(response.root_span_ptr)
690
+
659
691
  async def _process_push(self, response: Response) -> None:
660
692
  if response.HasField("closing_error") or not response.HasField("resp_pointer"):
661
693
  err_msg = (
glide/logger.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import traceback
5
6
  from enum import Enum
6
7
  from typing import Optional
7
8
 
@@ -20,13 +21,12 @@ class Level(Enum):
20
21
 
21
22
  class Logger:
22
23
  """
23
- A singleton class that allows logging which is consistent with logs from the internal rust core.
24
+ A singleton class that allows logging which is consistent with logs from the internal GLIDE core.
24
25
  The logger can be set up in 2 ways -
25
26
  1. By calling Logger.init, which configures the logger only if it wasn't previously configured.
26
27
  2. By calling Logger.set_logger_config, which replaces the existing configuration, and means that new logs will not be
27
28
  saved with the logs that were sent before the call.
28
- If set_logger_config wasn't called, the first log attempt will initialize a new logger with default configuration decided
29
- by the Rust core.
29
+ If none of these functions are called, the first log attempt will initialize a new logger with default configuration.
30
30
  """
31
31
 
32
32
  _instance = None
@@ -38,48 +38,60 @@ class Logger:
38
38
 
39
39
  @classmethod
40
40
  def init(cls, level: Optional[Level] = None, file_name: Optional[str] = None):
41
- """_summary_
41
+ """
42
42
  Initialize a logger if it wasn't initialized before - this method is meant to be used when there is no intention to
43
43
  replace an existing logger.
44
- The logger will filter all logs with a level lower than the given level,
45
- If given a fileName argument, will write the logs to files postfixed with fileName. If fileName isn't provided,
44
+ The logger will filter all logs with a level lower than the given level.
45
+ If given a file_name argument, will write the logs to files postfixed with file_name. If file_name isn't provided,
46
46
  the logs will be written to the console.
47
+
47
48
  Args:
48
49
  level (Optional[Level]): Set the logger level to one of [ERROR, WARN, INFO, DEBUG, TRACE, OFF].
49
- If log level isn't provided, the logger will be configured with default configuration decided by the Rust core.
50
- file_name (Optional[str]): If provided the target of the logs will be the file mentioned.
51
- Otherwise, logs will be printed to the console.
52
- To turn off logging completely, set the level to Level.OFF.
50
+ If log level isn't provided, the logger will be configured with default configuration.
51
+ To turn off logging completely, set the level to Level.OFF.
52
+ file_name (Optional[str]): If provided the target of the logs will be the file mentioned.
53
+ Otherwise, logs will be printed to the console.
53
54
  """
54
55
  if cls._instance is None:
55
56
  cls._instance = cls(level, file_name)
56
57
 
57
58
  @classmethod
58
- def log(cls, log_level: Level, log_identifier: str, message: str):
59
- """Logs the provided message if the provided log level is lower then the logger level.
59
+ def log(
60
+ cls,
61
+ log_level: Level,
62
+ log_identifier: str,
63
+ message: str,
64
+ err: Optional[Exception] = None,
65
+ ):
66
+ """
67
+ Logs the provided message if the provided log level is lower then the logger level.
60
68
 
61
69
  Args:
62
- log_level (Level): The log level of the provided message
70
+ log_level (Level): The log level of the provided message.
63
71
  log_identifier (str): The log identifier should give the log a context.
64
72
  message (str): The message to log.
73
+ err (Optional[Exception]): The exception or error to log.
65
74
  """
66
75
  if not cls._instance:
67
76
  cls._instance = cls(None)
68
77
  if not log_level.value.is_lower(Logger.logger_level):
69
78
  return
79
+ if err:
80
+ message = f"{message}: {traceback.format_exception(err)}"
70
81
  py_log(log_level.value, log_identifier, message)
71
82
 
72
83
  @classmethod
73
84
  def set_logger_config(
74
85
  cls, level: Optional[Level] = None, file_name: Optional[str] = None
75
86
  ):
76
- """Creates a new logger instance and configure it with the provided log level and file name.
87
+ """
88
+ Creates a new logger instance and configure it with the provided log level and file name.
77
89
 
78
90
  Args:
79
91
  level (Optional[Level]): Set the logger level to one of [ERROR, WARN, INFO, DEBUG, TRACE, OFF].
80
- If log level isn't provided, the logger will be configured with default configuration decided by the Rust core.
81
- file_name (Optional[str]): If provided the target of the logs will be the file mentioned.
82
- Otherwise, logs will be printed to the console.
83
- To turn off logging completely, set the level to OFF.
92
+ If log level isn't provided, the logger will be configured with default configuration.
93
+ To turn off logging completely, set the level to OFF.
94
+ file_name (Optional[str]): If provided the target of the logs will be the file mentioned.
95
+ Otherwise, logs will be printed to the console.
84
96
  """
85
97
  Logger._instance = Logger(level, file_name)