qena-shared-lib 0.1.17__py3-none-any.whl → 0.1.18__py3-none-any.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.
- qena_shared_lib/__init__.py +3 -2
- qena_shared_lib/application.py +4 -4
- qena_shared_lib/background.py +9 -7
- qena_shared_lib/exception_handling.py +409 -0
- qena_shared_lib/exceptions.py +170 -57
- qena_shared_lib/http/__init__.py +90 -0
- qena_shared_lib/{http.py → http/_base.py} +36 -36
- qena_shared_lib/http/_exception_handlers.py +202 -0
- qena_shared_lib/kafka/__init__.py +21 -0
- qena_shared_lib/kafka/_base.py +233 -0
- qena_shared_lib/kafka/_consumer.py +597 -0
- qena_shared_lib/kafka/_exception_handlers.py +124 -0
- qena_shared_lib/kafka/_producer.py +133 -0
- qena_shared_lib/logging.py +17 -13
- qena_shared_lib/rabbitmq/__init__.py +4 -6
- qena_shared_lib/rabbitmq/_base.py +68 -132
- qena_shared_lib/rabbitmq/_channel.py +2 -4
- qena_shared_lib/rabbitmq/_exception_handlers.py +69 -142
- qena_shared_lib/rabbitmq/_listener.py +246 -157
- qena_shared_lib/rabbitmq/_publisher.py +5 -5
- qena_shared_lib/rabbitmq/_rpc_client.py +21 -22
- qena_shared_lib/remotelogging/_base.py +20 -20
- qena_shared_lib/remotelogging/logstash/_base.py +2 -2
- qena_shared_lib/remotelogging/logstash/_http_sender.py +2 -4
- qena_shared_lib/remotelogging/logstash/_tcp_sender.py +2 -2
- qena_shared_lib/scheduler.py +24 -15
- qena_shared_lib/security.py +39 -32
- qena_shared_lib/utils.py +13 -11
- {qena_shared_lib-0.1.17.dist-info → qena_shared_lib-0.1.18.dist-info}/METADATA +4 -1
- qena_shared_lib-0.1.18.dist-info/RECORD +38 -0
- qena_shared_lib/exception_handlers.py +0 -235
- qena_shared_lib-0.1.17.dist-info/RECORD +0 -31
- {qena_shared_lib-0.1.17.dist-info → qena_shared_lib-0.1.18.dist-info}/WHEEL +0 -0
@@ -3,15 +3,13 @@ from asyncio import (
|
|
3
3
|
Future,
|
4
4
|
Task,
|
5
5
|
gather,
|
6
|
-
iscoroutinefunction,
|
7
6
|
)
|
8
|
-
from functools import partial
|
9
7
|
from random import uniform
|
8
|
+
from types import UnionType
|
10
9
|
from typing import (
|
11
10
|
Any,
|
12
11
|
Awaitable,
|
13
12
|
TypeVar,
|
14
|
-
cast,
|
15
13
|
)
|
16
14
|
|
17
15
|
try:
|
@@ -21,21 +19,23 @@ try:
|
|
21
19
|
from pika.frame import Method
|
22
20
|
except ImportError:
|
23
21
|
pass
|
24
|
-
from prometheus_client import Counter
|
25
22
|
from prometheus_client import Enum as PrometheusEnum
|
26
23
|
from punq import Container, Scope
|
27
24
|
|
28
|
-
from ..
|
29
|
-
|
25
|
+
from ..exception_handling import (
|
26
|
+
AbstractServiceExceptionHandler,
|
27
|
+
ExceptionHandlerServiceType,
|
28
|
+
ExceptionHandlingManager,
|
29
|
+
ServiceContext,
|
30
30
|
)
|
31
|
-
from ..
|
31
|
+
from ..exceptions import RabbitMQConnectionUnhealthyError
|
32
|
+
from ..logging import LoggerFactory
|
32
33
|
from ..remotelogging import BaseRemoteLogSender
|
33
34
|
from ..utils import AsyncEventLoopMixin
|
34
35
|
from ._exception_handlers import (
|
35
|
-
|
36
|
-
GeneralMqExceptionHandler,
|
36
|
+
RabbitMqGeneralExceptionHandler,
|
37
37
|
RabbitMqServiceExceptionHandler,
|
38
|
-
|
38
|
+
RabbitMqValidationErrorHandler,
|
39
39
|
)
|
40
40
|
from ._listener import (
|
41
41
|
LISTENER_ATTRIBUTE,
|
@@ -68,21 +68,16 @@ class AbstractRabbitMQService(ABC):
|
|
68
68
|
|
69
69
|
|
70
70
|
class RabbitMqManager(AsyncEventLoopMixin):
|
71
|
-
|
71
|
+
_RABBITMQ_CONNECTION_STATE = PrometheusEnum(
|
72
72
|
name="rabbitmq_connection_state",
|
73
73
|
documentation="Babbitmq connection state",
|
74
74
|
states=["connected", "reconnecting", "disconnected"],
|
75
75
|
)
|
76
|
-
|
76
|
+
_RABBITMQ_PUBLISHER_BLOCKED_STATE = PrometheusEnum(
|
77
77
|
name="rabbitmq_publisher_blocked_state",
|
78
78
|
documentation="Rabbitmq publisher blocked state",
|
79
79
|
states=["blocked", "unblocked"],
|
80
80
|
)
|
81
|
-
HANDLED_EXCEPTIONS = Counter(
|
82
|
-
name="handled_exceptions",
|
83
|
-
documentation="Handled exceptions",
|
84
|
-
labelnames=["queue", "listener_name", "exception"],
|
85
|
-
)
|
86
81
|
|
87
82
|
def __init__(
|
88
83
|
self,
|
@@ -109,39 +104,29 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
109
104
|
self._disconnected = False
|
110
105
|
self._connection_blocked = False
|
111
106
|
self._services: list[AbstractRabbitMQService] = []
|
112
|
-
self._exception_handlers: dict[
|
113
|
-
type[Exception], AbstractRabbitMqExceptionHandler
|
114
|
-
] = {}
|
115
107
|
self._channel_pool = ChannelPool()
|
116
108
|
self._remote_logger = remote_logger
|
117
|
-
self.
|
109
|
+
self._exception_handler = ExceptionHandlingManager(
|
110
|
+
service_type=ExceptionHandlerServiceType.RABBIT_MQ,
|
111
|
+
container=self._container,
|
112
|
+
remote_logger=self._remote_logger,
|
113
|
+
label_name=["queue", "listener_name", "exception"],
|
114
|
+
)
|
115
|
+
self._logger = LoggerFactory.get_logger("rabbitmq")
|
116
|
+
|
117
|
+
self._exception_handler.set_exception_handling_done_hook(
|
118
|
+
self._on_exception_handler_done
|
119
|
+
)
|
118
120
|
|
119
121
|
@property
|
120
122
|
def container(self) -> Container:
|
121
123
|
return self._container
|
122
124
|
|
123
|
-
def set_exception_handlers(
|
124
|
-
self, *exception_handlers: type[AbstractRabbitMqExceptionHandler]
|
125
|
-
) -> None:
|
126
|
-
for index, exception_handler in enumerate(exception_handlers):
|
127
|
-
if not isinstance(exception_handler, type) or not issubclass(
|
128
|
-
exception_handler, AbstractRabbitMqExceptionHandler
|
129
|
-
):
|
130
|
-
raise TypeError(
|
131
|
-
f"exception handler {index} is {type(exception_handler)}, expected instance of type or subclass of `AbstractRabbitMqExceptionHandler`"
|
132
|
-
)
|
133
|
-
|
134
|
-
self._container.register(
|
135
|
-
service=AbstractRabbitMqExceptionHandler,
|
136
|
-
factory=exception_handler,
|
137
|
-
scope=Scope.singleton,
|
138
|
-
)
|
139
|
-
|
140
125
|
def init_default_exception_handlers(self) -> None:
|
141
|
-
self.set_exception_handlers(
|
126
|
+
self._exception_handler.set_exception_handlers(
|
142
127
|
RabbitMqServiceExceptionHandler,
|
143
|
-
|
144
|
-
|
128
|
+
RabbitMqValidationErrorHandler,
|
129
|
+
RabbitMqGeneralExceptionHandler,
|
145
130
|
)
|
146
131
|
|
147
132
|
def include_listener(self, listener: Listener | type[ListenerBase]) -> None:
|
@@ -159,6 +144,11 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
159
144
|
f"listener is {type(listener)}, expected instance of type or subclass of `Listener` or `type[ListenerBase]`"
|
160
145
|
)
|
161
146
|
|
147
|
+
def set_exception_handlers(
|
148
|
+
self, *exception_handlers: type[AbstractServiceExceptionHandler]
|
149
|
+
) -> None:
|
150
|
+
self._exception_handler.set_exception_handlers(*exception_handlers)
|
151
|
+
|
162
152
|
def _register_listener_classes(self, listener_class: type) -> None:
|
163
153
|
inner_listener = getattr(listener_class, LISTENER_ATTRIBUTE, None)
|
164
154
|
|
@@ -204,7 +194,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
204
194
|
if not self._connected:
|
205
195
|
self._resolve_listener_classes()
|
206
196
|
self._resolve_service_classes()
|
207
|
-
self.
|
197
|
+
self._exception_handler.resolve_exception_handlers()
|
208
198
|
|
209
199
|
if self._is_connection_healthy():
|
210
200
|
raise RuntimeError("rabbitmq already connected and healthy")
|
@@ -231,23 +221,6 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
231
221
|
self._container.resolve_all(AbstractRabbitMQService)
|
232
222
|
)
|
233
223
|
|
234
|
-
def _resolve_exception_handlers(self) -> None:
|
235
|
-
for exception_handler in self._container.resolve_all(
|
236
|
-
AbstractRabbitMqExceptionHandler
|
237
|
-
):
|
238
|
-
exception_handler = cast(
|
239
|
-
AbstractRabbitMqExceptionHandler, exception_handler
|
240
|
-
)
|
241
|
-
|
242
|
-
if not callable(exception_handler):
|
243
|
-
raise ValueError(
|
244
|
-
f"exception handler {exception_handler.__class__.__name__} is not callable"
|
245
|
-
)
|
246
|
-
|
247
|
-
self._exception_handlers[exception_handler.exception] = (
|
248
|
-
exception_handler
|
249
|
-
)
|
250
|
-
|
251
224
|
@property
|
252
225
|
def connection(self) -> AsyncioConnection:
|
253
226
|
if not self._is_connection_healthy():
|
@@ -284,7 +257,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
284
257
|
exchange: str | None = None,
|
285
258
|
procedure: str | None = None,
|
286
259
|
headers: dict[str, str] | None = None,
|
287
|
-
return_type: type[R] | None = None,
|
260
|
+
return_type: type[R] | UnionType | None = None,
|
288
261
|
timeout: float = 15,
|
289
262
|
) -> RpcClient[R]:
|
290
263
|
if timeout == 0:
|
@@ -333,7 +306,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
333
306
|
self._connection.close()
|
334
307
|
self._logger.info("disconnected from rabbitmq")
|
335
308
|
|
336
|
-
self.
|
309
|
+
self._RABBITMQ_CONNECTION_STATE.state("disconnected")
|
337
310
|
|
338
311
|
async def _wait_for_listeners_and_services(self) -> None:
|
339
312
|
_ = await gather(
|
@@ -380,6 +353,8 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
380
353
|
if self._can_reconnect(exception):
|
381
354
|
self._remote_logger.error(
|
382
355
|
message="couldn't drain the channel pool",
|
356
|
+
tags=["rabbitmq", "channel_pool_drain_error"],
|
357
|
+
extra={"serviceType": "rabbitmq"},
|
383
358
|
exception=exception,
|
384
359
|
)
|
385
360
|
self._reconnect()
|
@@ -401,9 +376,11 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
401
376
|
self._connection_blocked = True
|
402
377
|
|
403
378
|
self._remote_logger.warning(
|
404
|
-
"connection is blocked by broker, will not accept published messages"
|
379
|
+
message="connection is blocked by broker, will not accept published messages",
|
380
|
+
tags=["rabbitmq", "connection_blocked"],
|
381
|
+
extra={"serviceType": "rabbitmq"},
|
405
382
|
)
|
406
|
-
self.
|
383
|
+
self._RABBITMQ_PUBLISHER_BLOCKED_STATE.state("blocked")
|
407
384
|
|
408
385
|
def _on_connection_unblocked(
|
409
386
|
self, connection: AsyncioConnection, method: Method
|
@@ -412,8 +389,12 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
412
389
|
|
413
390
|
self._connection_blocked = False
|
414
391
|
|
415
|
-
self._remote_logger.info(
|
416
|
-
|
392
|
+
self._remote_logger.info(
|
393
|
+
message="broker resumed accepting published messages",
|
394
|
+
tags=["rabbitmq", "connection_unblocked"],
|
395
|
+
extra={"serviceType": "rabbitmq"},
|
396
|
+
)
|
397
|
+
self._RABBITMQ_PUBLISHER_BLOCKED_STATE.state("unblocked")
|
417
398
|
|
418
399
|
def _is_connection_blocked(self) -> bool:
|
419
400
|
return self._connection_blocked
|
@@ -433,6 +414,8 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
433
414
|
elif self._can_reconnect(exception):
|
434
415
|
self._remote_logger.error(
|
435
416
|
message="couldn't fill the channel pool",
|
417
|
+
tags=["rabbitmq", "channel_pool_fill_error"],
|
418
|
+
extra={"serviceType": "rabbitmq"},
|
436
419
|
exception=exception,
|
437
420
|
)
|
438
421
|
self._reconnect()
|
@@ -463,7 +446,8 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
463
446
|
listener.configure(
|
464
447
|
connection=self._connection,
|
465
448
|
channel_pool=self._channel_pool,
|
466
|
-
on_exception_callback=self.
|
449
|
+
on_exception_callback=self._exception_handler.submit_exception,
|
450
|
+
blocked_connection_check_callback=self._is_connection_blocked,
|
467
451
|
container=self._container,
|
468
452
|
remote_logger=self._remote_logger,
|
469
453
|
global_retry_policy=self._listener_global_retry_policy,
|
@@ -517,6 +501,11 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
517
501
|
elif self._can_reconnect(exception):
|
518
502
|
self._remote_logger.error(
|
519
503
|
message="couldn't configure and initialize all listeners and services",
|
504
|
+
tags=[
|
505
|
+
"rabbitmq",
|
506
|
+
"listener_and_service_config_and_init_error",
|
507
|
+
],
|
508
|
+
extra={"serviceType": "rabbitmq"},
|
520
509
|
exception=exception,
|
521
510
|
)
|
522
511
|
self._reconnect()
|
@@ -547,7 +536,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
547
536
|
if not self._connected_future.done():
|
548
537
|
self._connected_future.set_result(None)
|
549
538
|
|
550
|
-
self.
|
539
|
+
self._RABBITMQ_CONNECTION_STATE.state("connected")
|
551
540
|
|
552
541
|
def _on_connection_open_error(
|
553
542
|
self, connection: AsyncioConnection, exception: BaseException
|
@@ -562,73 +551,14 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
562
551
|
if self._connected and not self._disconnected:
|
563
552
|
self._remote_logger.error(
|
564
553
|
message="error while opening connection to rabbitmq",
|
554
|
+
tags=["rabbitmq", "connection_open_error"],
|
555
|
+
extra={"serviceType": "rabbitmq"},
|
565
556
|
exception=exception,
|
566
557
|
)
|
567
558
|
self._reconnect()
|
568
559
|
|
569
|
-
def
|
570
|
-
|
571
|
-
) -> bool:
|
572
|
-
exception_handler = None
|
573
|
-
|
574
|
-
for exception_type in type(exception).mro():
|
575
|
-
exception_handler = self._exception_handlers.get(exception_type)
|
576
|
-
|
577
|
-
if exception_handler is not None:
|
578
|
-
break
|
579
|
-
|
580
|
-
if exception_handler is None:
|
581
|
-
return False
|
582
|
-
|
583
|
-
assert callable(exception_handler)
|
584
|
-
|
585
|
-
if self._is_async_exception_handler(exception_handler):
|
586
|
-
self.loop.create_task(
|
587
|
-
exception_handler(context, exception)
|
588
|
-
).add_done_callback(
|
589
|
-
partial(self._on_exception_handler_done, context)
|
590
|
-
)
|
591
|
-
else:
|
592
|
-
self.loop.run_in_executor(
|
593
|
-
executor=None,
|
594
|
-
func=partial(exception_handler, context, exception),
|
595
|
-
).add_done_callback(
|
596
|
-
partial(self._on_exception_handler_done, context)
|
597
|
-
)
|
598
|
-
|
599
|
-
self.HANDLED_EXCEPTIONS.labels(
|
600
|
-
queue=context.queue,
|
601
|
-
listener_name=context.listener_name,
|
602
|
-
exception=exception.__class__.__name__,
|
603
|
-
).inc()
|
604
|
-
|
605
|
-
return True
|
606
|
-
|
607
|
-
def _is_async_exception_handler(
|
608
|
-
self, exception_handler: AbstractRabbitMqExceptionHandler
|
609
|
-
) -> bool:
|
610
|
-
exception_handler_callable = getattr(
|
611
|
-
exception_handler, "__call__", None
|
612
|
-
)
|
613
|
-
|
614
|
-
if exception_handler_callable is None:
|
615
|
-
raise RuntimeError("exception handler has not `__call__` method")
|
616
|
-
|
617
|
-
return iscoroutinefunction(exception_handler_callable)
|
618
|
-
|
619
|
-
def _on_exception_handler_done(
|
620
|
-
self, context: ListenerContext, task_or_future: Task[Any] | Future[Any]
|
621
|
-
) -> None:
|
622
|
-
if task_or_future.cancelled():
|
623
|
-
return
|
624
|
-
|
625
|
-
exception = task_or_future.exception()
|
626
|
-
|
627
|
-
if exception is not None:
|
628
|
-
self._remote_logger.error(
|
629
|
-
message="error occured in listener exception handler",
|
630
|
-
exception=exception,
|
631
|
-
)
|
560
|
+
def _on_exception_handler_done(self, context: ServiceContext) -> None:
|
561
|
+
assert isinstance(context, ListenerContext)
|
632
562
|
|
633
563
|
context.dispose()
|
634
564
|
|
@@ -645,12 +575,14 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
645
575
|
if self._can_reconnect(exception):
|
646
576
|
self._remote_logger.error(
|
647
577
|
message="connection to rabbitmq closed unexpectedly, attempting to reconnect",
|
578
|
+
tags=["rabbitmq", "connection_closed"],
|
579
|
+
extra={"serviceType": "rabbitmq"},
|
648
580
|
exception=exception,
|
649
581
|
)
|
650
582
|
self._reconnect()
|
651
583
|
|
652
584
|
def _reconnect(self) -> None:
|
653
|
-
self.
|
585
|
+
self._RABBITMQ_CONNECTION_STATE.state("reconnecting")
|
654
586
|
self.loop.call_later(
|
655
587
|
delay=self._reconnect_delay
|
656
588
|
+ uniform(*self._reconnect_delay_jitter),
|
@@ -666,7 +598,9 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
666
598
|
self._connected_future.set_result(None)
|
667
599
|
|
668
600
|
self._remote_logger.exception(
|
669
|
-
"couldn't reconnect to rabbitmq, attempting to reconnect"
|
601
|
+
message="couldn't reconnect to rabbitmq, attempting to reconnect",
|
602
|
+
tags=["rabbitmq", "connection_open_error"],
|
603
|
+
extra={"serviceType": "rabbitmq"},
|
670
604
|
)
|
671
605
|
self._reconnect()
|
672
606
|
|
@@ -689,6 +623,8 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
689
623
|
if self._can_reconnect(exception):
|
690
624
|
self._remote_logger.error(
|
691
625
|
message="couldn't reconnect to rabbitmq, attempting to reconnect",
|
626
|
+
tags=["rabbitmq", "reconnect_error"],
|
627
|
+
extra={"serviceType": "rabbitmq"},
|
692
628
|
exception=exception,
|
693
629
|
)
|
694
630
|
self._reconnect()
|
@@ -12,7 +12,7 @@ try:
|
|
12
12
|
except ImportError:
|
13
13
|
pass
|
14
14
|
|
15
|
-
from ..logging import
|
15
|
+
from ..logging import LoggerFactory
|
16
16
|
from ..utils import AsyncEventLoopMixin
|
17
17
|
|
18
18
|
__all__ = ["BaseChannel"]
|
@@ -36,9 +36,7 @@ class BaseChannel(AsyncEventLoopMixin):
|
|
36
36
|
self._reopen_failures = 0
|
37
37
|
self._reserved = False
|
38
38
|
self._can_be_disposed = False
|
39
|
-
self._logger =
|
40
|
-
"rabbitmq.base_channel"
|
41
|
-
)
|
39
|
+
self._logger = LoggerFactory.get_logger("rabbitmq.base_channel")
|
42
40
|
|
43
41
|
def open(self) -> Future[UUID]:
|
44
42
|
if self._channel is not None:
|
@@ -1,182 +1,109 @@
|
|
1
|
-
from typing import cast
|
2
|
-
|
3
1
|
from pydantic import ValidationError
|
4
2
|
|
5
|
-
from ..
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
from ..exception_handling import (
|
4
|
+
ExceptionHandlerServiceType,
|
5
|
+
GeneralExceptionHandler,
|
6
|
+
ServiceExceptionHandler,
|
7
|
+
ServiceInformation,
|
8
|
+
ValidationErrorHandler,
|
10
9
|
)
|
11
|
-
from ..
|
12
|
-
from ..
|
10
|
+
from ..exceptions import ServiceException
|
11
|
+
from ..logging import LoggerFactory
|
12
|
+
from ..remotelogging import BaseRemoteLogSender
|
13
13
|
from ._listener import ListenerContext
|
14
14
|
|
15
15
|
__all__ = [
|
16
|
-
"AbstractRabbitMqExceptionHandler",
|
17
|
-
"GeneralMqExceptionHandler",
|
18
16
|
"RabbitMqServiceExceptionHandler",
|
19
|
-
"
|
17
|
+
"RabbitMqServiceExceptionHandler",
|
18
|
+
"RabbitMqValidationErrorHandler",
|
20
19
|
]
|
21
20
|
|
22
21
|
RABBITMQ_EXCEPTION_HANDLER_LOGGER_NAME = "rabbitmq.exception_handler"
|
23
22
|
|
24
23
|
|
25
|
-
class
|
26
|
-
|
27
|
-
|
28
|
-
raise NotImplementedError()
|
29
|
-
|
30
|
-
|
31
|
-
class RabbitMqServiceExceptionHandler(AbstractRabbitMqExceptionHandler):
|
32
|
-
@property
|
33
|
-
def exception(self) -> type[Exception]:
|
34
|
-
return cast(type[Exception], ServiceException)
|
24
|
+
class RabbitMqServiceExceptionHandler(ServiceExceptionHandler):
|
25
|
+
def __init__(self, remote_logger: BaseRemoteLogSender):
|
26
|
+
super().__init__(remote_logger)
|
35
27
|
|
36
|
-
|
37
|
-
self,
|
38
|
-
remote_logger: BaseRemoteLogSender,
|
39
|
-
logger_provider: LoggerProvider,
|
40
|
-
):
|
41
|
-
self._logger = logger_provider.get_logger(
|
28
|
+
self._logger = LoggerFactory.get_logger(
|
42
29
|
RABBITMQ_EXCEPTION_HANDLER_LOGGER_NAME
|
43
30
|
)
|
44
|
-
self._remote_logger = remote_logger
|
45
31
|
|
46
32
|
def __call__(
|
47
33
|
self,
|
48
34
|
context: ListenerContext,
|
49
35
|
exception: ServiceException,
|
50
36
|
) -> None:
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
tags.append(str_status_code)
|
71
|
-
|
72
|
-
if http_service_error.response_code is not None:
|
73
|
-
str_response_code = str(http_service_error.response_code)
|
74
|
-
extra["responseCode"] = str_response_code
|
75
|
-
|
76
|
-
tags.append(str_response_code)
|
77
|
-
case RabbitMQServiceException() as rabbitmq_service_exception:
|
78
|
-
str_error_code = str(rabbitmq_service_exception.code)
|
79
|
-
extra["code"] = str_error_code
|
80
|
-
|
81
|
-
tags.append(str_error_code)
|
82
|
-
|
83
|
-
if exception.tags:
|
84
|
-
tags.extend(exception.tags)
|
85
|
-
|
86
|
-
if exception.extra:
|
87
|
-
extra.update(exception.extra)
|
88
|
-
|
89
|
-
exc_info = (
|
90
|
-
(type(exception), exception, exception.__traceback__)
|
91
|
-
if exception.extract_exc_info
|
92
|
-
else None
|
37
|
+
self.handle(
|
38
|
+
service_information=ServiceInformation(
|
39
|
+
service_type=ExceptionHandlerServiceType.RABBIT_MQ,
|
40
|
+
tags=[
|
41
|
+
"RabbitMQ",
|
42
|
+
context.queue,
|
43
|
+
context.listener_name or "__default__",
|
44
|
+
exception.__class__.__name__,
|
45
|
+
],
|
46
|
+
extra={
|
47
|
+
"serviceType": "RabbitMQ",
|
48
|
+
"queue": context.queue,
|
49
|
+
"listenerName": context.listener_name,
|
50
|
+
"exception": exception.__class__.__name__,
|
51
|
+
},
|
52
|
+
message=f"queue = `{context.queue}` listener_name = `{context.listener_name}` {exception.message}",
|
53
|
+
),
|
54
|
+
exception=exception,
|
93
55
|
)
|
94
56
|
|
95
|
-
match exception.severity:
|
96
|
-
case Severity.HIGH:
|
97
|
-
remote_logger_method = self._remote_logger.error
|
98
|
-
logger_method = self._logger.error
|
99
|
-
case Severity.MEDIUM:
|
100
|
-
remote_logger_method = self._remote_logger.warning
|
101
|
-
logger_method = self._logger.warning
|
102
|
-
case _:
|
103
|
-
remote_logger_method = self._remote_logger.info
|
104
|
-
logger_method = self._logger.info
|
105
|
-
|
106
|
-
if exception.remote_logging:
|
107
|
-
remote_logger_method(
|
108
|
-
message=exception.message,
|
109
|
-
tags=tags,
|
110
|
-
extra=extra,
|
111
|
-
exception=exception if exception.extract_exc_info else None,
|
112
|
-
)
|
113
|
-
else:
|
114
|
-
logger_method(
|
115
|
-
"\nRabbitMQ `%s` -> `%s`\n%s",
|
116
|
-
context.queue,
|
117
|
-
context.listener_name,
|
118
|
-
exception.message,
|
119
|
-
exc_info=exc_info,
|
120
|
-
)
|
121
|
-
|
122
|
-
|
123
|
-
class ValidationErrorHandler(AbstractRabbitMqExceptionHandler):
|
124
|
-
@property
|
125
|
-
def exception(self) -> type[Exception]:
|
126
|
-
return cast(type[Exception], ValidationError)
|
127
|
-
|
128
|
-
def __init__(self, remote_logger: BaseRemoteLogSender):
|
129
|
-
self._remote_logger = remote_logger
|
130
57
|
|
58
|
+
class RabbitMqValidationErrorHandler(ValidationErrorHandler):
|
131
59
|
def __call__(
|
132
60
|
self,
|
133
61
|
context: ListenerContext,
|
134
62
|
exception: ValidationError,
|
135
63
|
) -> None:
|
136
|
-
self.
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
64
|
+
self.handle(
|
65
|
+
service_information=ServiceInformation(
|
66
|
+
service_type=ExceptionHandlerServiceType.RABBIT_MQ,
|
67
|
+
tags=[
|
68
|
+
"RabbitMQ",
|
69
|
+
context.queue,
|
70
|
+
context.listener_name or "__default__",
|
71
|
+
"ValidationError",
|
72
|
+
],
|
73
|
+
extra={
|
74
|
+
"serviceType": "RabbitMQ",
|
75
|
+
"queue": context.queue,
|
76
|
+
"listenerName": context.listener_name,
|
77
|
+
"exception": "ValidationError",
|
78
|
+
},
|
79
|
+
message=f"invalid rabbitmq request data at queue `{context.queue}` and listener `{context.listener_name}`",
|
80
|
+
),
|
150
81
|
exception=exception,
|
151
82
|
)
|
152
83
|
|
153
84
|
|
154
|
-
class
|
155
|
-
@property
|
156
|
-
def exception(self) -> type[Exception]:
|
157
|
-
return Exception
|
158
|
-
|
159
|
-
def __init__(self, remote_logger: BaseRemoteLogSender):
|
160
|
-
self._remote_logger = remote_logger
|
161
|
-
|
85
|
+
class RabbitMqGeneralExceptionHandler(GeneralExceptionHandler):
|
162
86
|
def __call__(
|
163
87
|
self,
|
164
88
|
context: ListenerContext,
|
165
89
|
exception: Exception,
|
166
90
|
) -> None:
|
167
|
-
self.
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
91
|
+
self.handle(
|
92
|
+
service_information=ServiceInformation(
|
93
|
+
service_type=ExceptionHandlerServiceType.RABBIT_MQ,
|
94
|
+
tags=[
|
95
|
+
"RabbitMQ",
|
96
|
+
context.queue,
|
97
|
+
context.listener_name or "__default__",
|
98
|
+
exception.__class__.__name__,
|
99
|
+
],
|
100
|
+
extra={
|
101
|
+
"serviceType": "RabbitMQ",
|
102
|
+
"queue": context.queue,
|
103
|
+
"listenerName": context.listener_name,
|
104
|
+
"exception": exception.__class__.__name__,
|
105
|
+
},
|
106
|
+
message=f"something went wrong while consuming message on queue `{context.queue}` and listener `{context.listener_name}`",
|
107
|
+
),
|
181
108
|
exception=exception,
|
182
109
|
)
|