omnibase_infra 0.2.1__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.
- omnibase_infra/__init__.py +101 -0
- omnibase_infra/cli/__init__.py +1 -0
- omnibase_infra/cli/commands.py +216 -0
- omnibase_infra/clients/__init__.py +0 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +261 -0
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +138 -0
- omnibase_infra/decorators/__init__.py +29 -0
- omnibase_infra/decorators/allow_any.py +109 -0
- omnibase_infra/dlq/__init__.py +90 -0
- omnibase_infra/dlq/constants_dlq.py +57 -0
- omnibase_infra/dlq/models/__init__.py +26 -0
- omnibase_infra/dlq/models/enum_replay_status.py +37 -0
- omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
- omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
- omnibase_infra/dlq/service_dlq_tracking.py +611 -0
- omnibase_infra/enums/__init__.py +123 -0
- omnibase_infra/enums/enum_any_type_violation.py +104 -0
- omnibase_infra/enums/enum_backend_type.py +27 -0
- omnibase_infra/enums/enum_capture_outcome.py +42 -0
- omnibase_infra/enums/enum_capture_state.py +88 -0
- omnibase_infra/enums/enum_chain_violation_type.py +119 -0
- omnibase_infra/enums/enum_circuit_state.py +51 -0
- omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
- omnibase_infra/enums/enum_contract_type.py +84 -0
- omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
- omnibase_infra/enums/enum_dispatch_status.py +191 -0
- omnibase_infra/enums/enum_environment.py +46 -0
- omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
- omnibase_infra/enums/enum_handler_error_type.py +101 -0
- omnibase_infra/enums/enum_handler_loader_error.py +178 -0
- omnibase_infra/enums/enum_handler_source_type.py +87 -0
- omnibase_infra/enums/enum_handler_type.py +77 -0
- omnibase_infra/enums/enum_handler_type_category.py +61 -0
- omnibase_infra/enums/enum_infra_transport_type.py +73 -0
- omnibase_infra/enums/enum_introspection_reason.py +154 -0
- omnibase_infra/enums/enum_message_category.py +213 -0
- omnibase_infra/enums/enum_node_archetype.py +74 -0
- omnibase_infra/enums/enum_node_output_type.py +185 -0
- omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
- omnibase_infra/enums/enum_policy_type.py +32 -0
- omnibase_infra/enums/enum_registration_state.py +261 -0
- omnibase_infra/enums/enum_registration_status.py +33 -0
- omnibase_infra/enums/enum_registry_response_status.py +28 -0
- omnibase_infra/enums/enum_response_status.py +26 -0
- omnibase_infra/enums/enum_retry_error_category.py +98 -0
- omnibase_infra/enums/enum_security_rule_id.py +103 -0
- omnibase_infra/enums/enum_selection_strategy.py +91 -0
- omnibase_infra/enums/enum_topic_standard.py +42 -0
- omnibase_infra/enums/enum_validation_severity.py +78 -0
- omnibase_infra/errors/__init__.py +156 -0
- omnibase_infra/errors/error_architecture_violation.py +152 -0
- omnibase_infra/errors/error_chain_propagation.py +188 -0
- omnibase_infra/errors/error_compute_registry.py +92 -0
- omnibase_infra/errors/error_consul.py +132 -0
- omnibase_infra/errors/error_container_wiring.py +243 -0
- omnibase_infra/errors/error_event_bus_registry.py +102 -0
- omnibase_infra/errors/error_infra.py +608 -0
- omnibase_infra/errors/error_message_type_registry.py +101 -0
- omnibase_infra/errors/error_policy_registry.py +112 -0
- omnibase_infra/errors/error_vault.py +123 -0
- omnibase_infra/event_bus/__init__.py +72 -0
- omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +86 -0
- omnibase_infra/event_bus/event_bus_inmemory.py +743 -0
- omnibase_infra/event_bus/event_bus_kafka.py +1658 -0
- omnibase_infra/event_bus/mixin_kafka_broadcast.py +184 -0
- omnibase_infra/event_bus/mixin_kafka_dlq.py +765 -0
- omnibase_infra/event_bus/models/__init__.py +29 -0
- omnibase_infra/event_bus/models/config/__init__.py +20 -0
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +725 -0
- omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
- omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
- omnibase_infra/event_bus/models/model_event_headers.py +115 -0
- omnibase_infra/event_bus/models/model_event_message.py +60 -0
- omnibase_infra/event_bus/topic_constants.py +376 -0
- omnibase_infra/handlers/__init__.py +75 -0
- omnibase_infra/handlers/filesystem/__init__.py +48 -0
- omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
- omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
- omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
- omnibase_infra/handlers/handler_consul.py +787 -0
- omnibase_infra/handlers/handler_db.py +1039 -0
- omnibase_infra/handlers/handler_filesystem.py +1478 -0
- omnibase_infra/handlers/handler_graph.py +1154 -0
- omnibase_infra/handlers/handler_http.py +920 -0
- omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
- omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
- omnibase_infra/handlers/handler_mcp.py +748 -0
- omnibase_infra/handlers/handler_qdrant.py +1076 -0
- omnibase_infra/handlers/handler_vault.py +422 -0
- omnibase_infra/handlers/mcp/__init__.py +19 -0
- omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
- omnibase_infra/handlers/mcp/protocols.py +178 -0
- omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
- omnibase_infra/handlers/mixins/__init__.py +42 -0
- omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +337 -0
- omnibase_infra/handlers/mixins/mixin_consul_service.py +277 -0
- omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
- omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
- omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
- omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
- omnibase_infra/handlers/models/__init__.py +286 -0
- omnibase_infra/handlers/models/consul/__init__.py +81 -0
- omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
- omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
- omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
- omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
- omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
- omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
- omnibase_infra/handlers/models/graph/__init__.py +35 -0
- omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
- omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
- omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
- omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
- omnibase_infra/handlers/models/http/__init__.py +50 -0
- omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
- omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
- omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
- omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
- omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
- omnibase_infra/handlers/models/mcp/__init__.py +23 -0
- omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
- omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
- omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
- omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
- omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
- omnibase_infra/handlers/models/model_db_query_response.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
- omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
- omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
- omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
- omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
- omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
- omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
- omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_handler_response.py +103 -0
- omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
- omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
- omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
- omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
- omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
- omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
- omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
- omnibase_infra/handlers/models/model_operation_context.py +187 -0
- omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_retry_state.py +162 -0
- omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
- omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
- omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
- omnibase_infra/handlers/models/vault/__init__.py +69 -0
- omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
- omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
- omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
- omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
- omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
- omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
- omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
- omnibase_infra/handlers/registration_storage/__init__.py +43 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +915 -0
- omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
- omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
- omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
- omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
- omnibase_infra/handlers/service_discovery/__init__.py +43 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +747 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
- omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
- omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
- omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
- omnibase_infra/handlers/service_discovery/models/model_service_info.py +99 -0
- omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
- omnibase_infra/idempotency/__init__.py +94 -0
- omnibase_infra/idempotency/models/__init__.py +43 -0
- omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
- omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
- omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
- omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
- omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
- omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
- omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
- omnibase_infra/idempotency/store_inmemory.py +265 -0
- omnibase_infra/idempotency/store_postgres.py +923 -0
- omnibase_infra/infrastructure/__init__.py +0 -0
- omnibase_infra/mixins/__init__.py +71 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +655 -0
- omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
- omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
- omnibase_infra/mixins/mixin_node_introspection.py +2465 -0
- omnibase_infra/mixins/mixin_retry_execution.py +386 -0
- omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
- omnibase_infra/models/__init__.py +136 -0
- omnibase_infra/models/corpus/__init__.py +17 -0
- omnibase_infra/models/corpus/model_capture_config.py +133 -0
- omnibase_infra/models/corpus/model_capture_result.py +86 -0
- omnibase_infra/models/discovery/__init__.py +42 -0
- omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
- omnibase_infra/models/discovery/model_introspection_config.py +311 -0
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
- omnibase_infra/models/dispatch/__init__.py +147 -0
- omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
- omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
- omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
- omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
- omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
- omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
- omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
- omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
- omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
- omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
- omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
- omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
- omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
- omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
- omnibase_infra/models/errors/__init__.py +45 -0
- omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
- omnibase_infra/models/errors/model_infra_error_context.py +99 -0
- omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
- omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
- omnibase_infra/models/handlers/__init__.py +37 -0
- omnibase_infra/models/handlers/model_contract_discovery_result.py +80 -0
- omnibase_infra/models/handlers/model_handler_descriptor.py +185 -0
- omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
- omnibase_infra/models/health/__init__.py +9 -0
- omnibase_infra/models/health/model_health_check_result.py +40 -0
- omnibase_infra/models/lifecycle/__init__.py +39 -0
- omnibase_infra/models/logging/__init__.py +51 -0
- omnibase_infra/models/logging/model_log_context.py +756 -0
- omnibase_infra/models/model_retry_error_classification.py +78 -0
- omnibase_infra/models/projection/__init__.py +43 -0
- omnibase_infra/models/projection/model_capability_fields.py +112 -0
- omnibase_infra/models/projection/model_registration_projection.py +434 -0
- omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
- omnibase_infra/models/projection/model_sequence_info.py +182 -0
- omnibase_infra/models/projection/model_snapshot_topic_config.py +590 -0
- omnibase_infra/models/projectors/__init__.py +41 -0
- omnibase_infra/models/projectors/model_projector_column.py +289 -0
- omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
- omnibase_infra/models/projectors/model_projector_index.py +270 -0
- omnibase_infra/models/projectors/model_projector_schema.py +415 -0
- omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
- omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
- omnibase_infra/models/registration/__init__.py +59 -0
- omnibase_infra/models/registration/commands/__init__.py +15 -0
- omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
- omnibase_infra/models/registration/events/__init__.py +56 -0
- omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
- omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
- omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
- omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
- omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
- omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
- omnibase_infra/models/registration/model_node_capabilities.py +179 -0
- omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +175 -0
- omnibase_infra/models/registration/model_node_metadata.py +79 -0
- omnibase_infra/models/registration/model_node_registration.py +162 -0
- omnibase_infra/models/registration/model_node_registration_record.py +162 -0
- omnibase_infra/models/registry/__init__.py +29 -0
- omnibase_infra/models/registry/model_domain_constraint.py +202 -0
- omnibase_infra/models/registry/model_message_type_entry.py +271 -0
- omnibase_infra/models/resilience/__init__.py +9 -0
- omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
- omnibase_infra/models/routing/__init__.py +25 -0
- omnibase_infra/models/routing/model_routing_entry.py +52 -0
- omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
- omnibase_infra/models/runtime/__init__.py +40 -0
- omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
- omnibase_infra/models/runtime/model_discovery_error.py +81 -0
- omnibase_infra/models/runtime/model_discovery_result.py +162 -0
- omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
- omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
- omnibase_infra/models/runtime/model_handler_contract.py +280 -0
- omnibase_infra/models/runtime/model_loaded_handler.py +120 -0
- omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
- omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
- omnibase_infra/models/security/__init__.py +50 -0
- omnibase_infra/models/security/classification_levels.py +99 -0
- omnibase_infra/models/security/model_environment_policy.py +145 -0
- omnibase_infra/models/security/model_handler_security_policy.py +107 -0
- omnibase_infra/models/security/model_security_error.py +81 -0
- omnibase_infra/models/security/model_security_validation_result.py +328 -0
- omnibase_infra/models/security/model_security_warning.py +67 -0
- omnibase_infra/models/snapshot/__init__.py +27 -0
- omnibase_infra/models/snapshot/model_field_change.py +65 -0
- omnibase_infra/models/snapshot/model_snapshot.py +270 -0
- omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
- omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
- omnibase_infra/models/types/__init__.py +71 -0
- omnibase_infra/models/validation/__init__.py +89 -0
- omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
- omnibase_infra/models/validation/model_any_type_violation.py +141 -0
- omnibase_infra/models/validation/model_category_match_result.py +345 -0
- omnibase_infra/models/validation/model_chain_violation.py +166 -0
- omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
- omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
- omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
- omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
- omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
- omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
- omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
- omnibase_infra/models/validation/model_output_validation_params.py +74 -0
- omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
- omnibase_infra/models/validation/model_validation_error_params.py +84 -0
- omnibase_infra/models/validation/model_validation_outcome.py +287 -0
- omnibase_infra/nodes/__init__.py +48 -0
- omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
- omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
- omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +208 -0
- omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
- omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
- omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
- omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
- omnibase_infra/nodes/architecture_validator/node.py +262 -0
- omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
- omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
- omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
- omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
- omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +99 -0
- omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
- omnibase_infra/nodes/effects/README.md +358 -0
- omnibase_infra/nodes/effects/__init__.py +26 -0
- omnibase_infra/nodes/effects/contract.yaml +172 -0
- omnibase_infra/nodes/effects/models/__init__.py +32 -0
- omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
- omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
- omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
- omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
- omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
- omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
- omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
- omnibase_infra/nodes/effects/registry_effect.py +525 -0
- omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
- omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
- omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
- omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +475 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +609 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
- omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
- omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
- omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +525 -0
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +392 -0
- omnibase_infra/nodes/node_registration_orchestrator/wiring.py +742 -0
- omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
- omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
- omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
- omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
- omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
- omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +225 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
- omnibase_infra/nodes/node_registration_storage_effect/node.py +109 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +194 -0
- omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
- omnibase_infra/nodes/node_registry_effect/contract.yaml +682 -0
- omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +416 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
- omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
- omnibase_infra/nodes/node_registry_effect/node.py +165 -0
- omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
- omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
- omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +214 -0
- omnibase_infra/nodes/reducers/__init__.py +30 -0
- omnibase_infra/nodes/reducers/models/__init__.py +32 -0
- omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +76 -0
- omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
- omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
- omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
- omnibase_infra/nodes/reducers/registration_reducer.py +1137 -0
- omnibase_infra/observability/__init__.py +143 -0
- omnibase_infra/observability/constants_metrics.py +91 -0
- omnibase_infra/observability/factory_observability_sink.py +525 -0
- omnibase_infra/observability/handlers/__init__.py +118 -0
- omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
- omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
- omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
- omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
- omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
- omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
- omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
- omnibase_infra/observability/hooks/__init__.py +74 -0
- omnibase_infra/observability/hooks/hook_observability.py +1223 -0
- omnibase_infra/observability/models/__init__.py +30 -0
- omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
- omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
- omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
- omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
- omnibase_infra/observability/sinks/__init__.py +69 -0
- omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
- omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
- omnibase_infra/plugins/__init__.py +27 -0
- omnibase_infra/plugins/examples/__init__.py +28 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
- omnibase_infra/plugins/models/__init__.py +21 -0
- omnibase_infra/plugins/models/model_plugin_context.py +76 -0
- omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
- omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
- omnibase_infra/plugins/plugin_compute_base.py +435 -0
- omnibase_infra/projectors/__init__.py +30 -0
- omnibase_infra/projectors/contracts/__init__.py +63 -0
- omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
- omnibase_infra/projectors/projection_reader_registration.py +1559 -0
- omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
- omnibase_infra/protocols/__init__.py +99 -0
- omnibase_infra/protocols/protocol_capability_projection.py +253 -0
- omnibase_infra/protocols/protocol_capability_query.py +251 -0
- omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
- omnibase_infra/protocols/protocol_event_projector.py +96 -0
- omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
- omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
- omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
- omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
- omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
- omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
- omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
- omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
- omnibase_infra/runtime/__init__.py +296 -0
- omnibase_infra/runtime/binding_config_resolver.py +2706 -0
- omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
- omnibase_infra/runtime/contract_handler_discovery.py +582 -0
- omnibase_infra/runtime/contract_loaders/__init__.py +42 -0
- omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
- omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
- omnibase_infra/runtime/enums/__init__.py +18 -0
- omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
- omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
- omnibase_infra/runtime/envelope_validator.py +179 -0
- omnibase_infra/runtime/handler_contract_source.py +669 -0
- omnibase_infra/runtime/handler_plugin_loader.py +2029 -0
- omnibase_infra/runtime/handler_registry.py +321 -0
- omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
- omnibase_infra/runtime/kernel.py +40 -0
- omnibase_infra/runtime/mixin_policy_validation.py +522 -0
- omnibase_infra/runtime/mixin_semver_cache.py +378 -0
- omnibase_infra/runtime/mixins/__init__.py +17 -0
- omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +757 -0
- omnibase_infra/runtime/models/__init__.py +192 -0
- omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
- omnibase_infra/runtime/models/model_binding_config.py +168 -0
- omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
- omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
- omnibase_infra/runtime/models/model_cached_secret.py +138 -0
- omnibase_infra/runtime/models/model_compute_key.py +138 -0
- omnibase_infra/runtime/models/model_compute_registration.py +97 -0
- omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
- omnibase_infra/runtime/models/model_config_ref.py +331 -0
- omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
- omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
- omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
- omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
- omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
- omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
- omnibase_infra/runtime/models/model_failed_component.py +55 -0
- omnibase_infra/runtime/models/model_health_check_response.py +168 -0
- omnibase_infra/runtime/models/model_health_check_result.py +228 -0
- omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
- omnibase_infra/runtime/models/model_logging_config.py +42 -0
- omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
- omnibase_infra/runtime/models/model_optional_string.py +94 -0
- omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
- omnibase_infra/runtime/models/model_policy_context.py +100 -0
- omnibase_infra/runtime/models/model_policy_key.py +138 -0
- omnibase_infra/runtime/models/model_policy_registration.py +139 -0
- omnibase_infra/runtime/models/model_policy_result.py +103 -0
- omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
- omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
- omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
- omnibase_infra/runtime/models/model_retry_policy.py +105 -0
- omnibase_infra/runtime/models/model_runtime_config.py +150 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_config.py +624 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
- omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
- omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
- omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
- omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
- omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
- omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
- omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
- omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
- omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
- omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
- omnibase_infra/runtime/projector_schema_manager.py +565 -0
- omnibase_infra/runtime/projector_shell.py +1102 -0
- omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
- omnibase_infra/runtime/protocol_contract_source.py +92 -0
- omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
- omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
- omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
- omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
- omnibase_infra/runtime/protocol_policy.py +366 -0
- omnibase_infra/runtime/protocols/__init__.py +27 -0
- omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
- omnibase_infra/runtime/registry/__init__.py +93 -0
- omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
- omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
- omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
- omnibase_infra/runtime/registry/registry_message_type.py +542 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +444 -0
- omnibase_infra/runtime/registry_compute.py +1143 -0
- omnibase_infra/runtime/registry_dispatcher.py +678 -0
- omnibase_infra/runtime/registry_policy.py +1502 -0
- omnibase_infra/runtime/runtime_scheduler.py +1070 -0
- omnibase_infra/runtime/secret_resolver.py +2110 -0
- omnibase_infra/runtime/security_metadata_validator.py +776 -0
- omnibase_infra/runtime/service_kernel.py +1573 -0
- omnibase_infra/runtime/service_message_dispatch_engine.py +1805 -0
- omnibase_infra/runtime/service_runtime_host_process.py +2260 -0
- omnibase_infra/runtime/util_container_wiring.py +1123 -0
- omnibase_infra/runtime/util_validation.py +314 -0
- omnibase_infra/runtime/util_version.py +98 -0
- omnibase_infra/runtime/util_wiring.py +566 -0
- omnibase_infra/schemas/schema_registration_projection.sql +320 -0
- omnibase_infra/services/__init__.py +68 -0
- omnibase_infra/services/corpus_capture.py +678 -0
- omnibase_infra/services/service_capability_query.py +945 -0
- omnibase_infra/services/service_health.py +897 -0
- omnibase_infra/services/service_node_selector.py +530 -0
- omnibase_infra/services/service_timeout_emitter.py +682 -0
- omnibase_infra/services/service_timeout_scanner.py +390 -0
- omnibase_infra/services/snapshot/__init__.py +31 -0
- omnibase_infra/services/snapshot/service_snapshot.py +647 -0
- omnibase_infra/services/snapshot/store_inmemory.py +637 -0
- omnibase_infra/services/snapshot/store_postgres.py +1279 -0
- omnibase_infra/shared/__init__.py +8 -0
- omnibase_infra/testing/__init__.py +10 -0
- omnibase_infra/testing/utils.py +23 -0
- omnibase_infra/types/__init__.py +48 -0
- omnibase_infra/types/type_cache_info.py +49 -0
- omnibase_infra/types/type_dsn.py +173 -0
- omnibase_infra/types/type_infra_aliases.py +60 -0
- omnibase_infra/types/typed_dict/__init__.py +21 -0
- omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
- omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
- omnibase_infra/types/typed_dict_capabilities.py +64 -0
- omnibase_infra/utils/__init__.py +89 -0
- omnibase_infra/utils/correlation.py +208 -0
- omnibase_infra/utils/util_datetime.py +372 -0
- omnibase_infra/utils/util_dsn_validation.py +333 -0
- omnibase_infra/utils/util_env_parsing.py +264 -0
- omnibase_infra/utils/util_error_sanitization.py +457 -0
- omnibase_infra/utils/util_pydantic_validators.py +477 -0
- omnibase_infra/utils/util_semver.py +233 -0
- omnibase_infra/validation/__init__.py +307 -0
- omnibase_infra/validation/enums/__init__.py +11 -0
- omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
- omnibase_infra/validation/infra_validators.py +1486 -0
- omnibase_infra/validation/linter_contract.py +907 -0
- omnibase_infra/validation/mixin_any_type_classification.py +120 -0
- omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
- omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
- omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
- omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
- omnibase_infra/validation/models/__init__.py +15 -0
- omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
- omnibase_infra/validation/models/model_contract_violation.py +41 -0
- omnibase_infra/validation/service_validation_aggregator.py +395 -0
- omnibase_infra/validation/validation_exemptions.yaml +1710 -0
- omnibase_infra/validation/validator_any_type.py +715 -0
- omnibase_infra/validation/validator_chain_propagation.py +839 -0
- omnibase_infra/validation/validator_execution_shape.py +465 -0
- omnibase_infra/validation/validator_localhandler.py +261 -0
- omnibase_infra/validation/validator_registration_security.py +410 -0
- omnibase_infra/validation/validator_routing_coverage.py +1020 -0
- omnibase_infra/validation/validator_runtime_shape.py +915 -0
- omnibase_infra/validation/validator_security.py +410 -0
- omnibase_infra/validation/validator_topic_category.py +1152 -0
- omnibase_infra-0.2.1.dist-info/METADATA +197 -0
- omnibase_infra-0.2.1.dist-info/RECORD +675 -0
- omnibase_infra-0.2.1.dist-info/WHEEL +4 -0
- omnibase_infra-0.2.1.dist-info/entry_points.txt +4 -0
- omnibase_infra-0.2.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,967 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Structured Logging Handler - EFFECT handler for structured logging with buffer/flush.
|
|
4
|
+
|
|
5
|
+
This handler implements the ONEX EFFECT handler pattern for structured logging.
|
|
6
|
+
It manages the lifecycle of a SinkLoggingStructured instance, providing:
|
|
7
|
+
- Contract-driven lifecycle management (initialize, shutdown)
|
|
8
|
+
- Periodic background flush with configurable interval
|
|
9
|
+
- Buffer threshold flush (when buffer reaches capacity)
|
|
10
|
+
- Graceful shutdown with final flush
|
|
11
|
+
|
|
12
|
+
Architecture Principle: "Handlers own lifecycle, sinks own hot path"
|
|
13
|
+
- This handler: Contract-driven lifecycle, buffer/flush management
|
|
14
|
+
- SinkLoggingStructured: Fast in-process emission (synchronous, non-blocking)
|
|
15
|
+
|
|
16
|
+
Supported Operations:
|
|
17
|
+
- logging.emit: Emit a log entry to the buffer (delegates to sink)
|
|
18
|
+
- logging.flush: Force flush all buffered entries immediately
|
|
19
|
+
- logging.configure: Update logging configuration at runtime
|
|
20
|
+
|
|
21
|
+
Thread Safety:
|
|
22
|
+
The handler uses a multi-lock design for safe concurrent access:
|
|
23
|
+
|
|
24
|
+
1. _config_lock (asyncio.Lock): Guards async configuration changes and prevents
|
|
25
|
+
concurrent configure operations from interfering with each other.
|
|
26
|
+
|
|
27
|
+
2. _emit_lock (threading.Lock): Guards the critical section during emit and
|
|
28
|
+
reconfiguration to prevent log loss. This lock ensures that:
|
|
29
|
+
- While an emit is in progress, reconfiguration waits for it to complete
|
|
30
|
+
- While reconfiguration is in progress (swap + flush), emits are serialized
|
|
31
|
+
|
|
32
|
+
The underlying sink uses its own threading.Lock for buffer operations.
|
|
33
|
+
This multi-lock design allows safe operation from both async and sync contexts
|
|
34
|
+
while preventing log loss during reconfiguration.
|
|
35
|
+
|
|
36
|
+
Reconfiguration Log Loss Prevention:
|
|
37
|
+
Without synchronization, a race condition can cause log loss:
|
|
38
|
+
1. emit reads self._sink -> gets old_sink reference
|
|
39
|
+
2. configure swaps self._sink to new_sink
|
|
40
|
+
3. emit calls old_sink.emit() -> log goes to abandoned sink (LOST)
|
|
41
|
+
|
|
42
|
+
The _emit_lock prevents this by ensuring the swap is atomic with respect
|
|
43
|
+
to emit operations. The swap-then-flush sequence is:
|
|
44
|
+
1. Acquire _emit_lock (blocks all emits)
|
|
45
|
+
2. Swap to new sink (atomic reference swap)
|
|
46
|
+
3. Release _emit_lock (emits resume to new sink immediately)
|
|
47
|
+
4. Flush old sink (outside lock - logs during flush go to new sink)
|
|
48
|
+
|
|
49
|
+
This guarantees no log loss while minimizing lock hold time. The old sink's
|
|
50
|
+
buffer is preserved until explicitly flushed, and any logs emitted during
|
|
51
|
+
the flush operation go directly to the new sink.
|
|
52
|
+
|
|
53
|
+
Drop Policy:
|
|
54
|
+
The drop_policy configuration field accepts only "drop_oldest", which is the
|
|
55
|
+
sole supported policy. When the buffer is full, the oldest entries are dropped
|
|
56
|
+
to make room for new ones, preserving recent logs. The policy is applied to
|
|
57
|
+
the sink during both initialization and reconfiguration.
|
|
58
|
+
|
|
59
|
+
Envelope-Based Routing:
|
|
60
|
+
This handler uses envelope-based operation routing. See CLAUDE.md section
|
|
61
|
+
"Intent Model Architecture > Envelope-Based Handler Routing" for the full
|
|
62
|
+
design pattern.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
from __future__ import annotations
|
|
66
|
+
|
|
67
|
+
import asyncio
|
|
68
|
+
import logging
|
|
69
|
+
import threading
|
|
70
|
+
from typing import TYPE_CHECKING
|
|
71
|
+
from uuid import UUID, uuid4
|
|
72
|
+
|
|
73
|
+
from omnibase_core.types import JsonType
|
|
74
|
+
from omnibase_infra.enums import (
|
|
75
|
+
EnumHandlerType,
|
|
76
|
+
EnumHandlerTypeCategory,
|
|
77
|
+
EnumInfraTransportType,
|
|
78
|
+
EnumResponseStatus,
|
|
79
|
+
)
|
|
80
|
+
from omnibase_infra.errors import (
|
|
81
|
+
ModelInfraErrorContext,
|
|
82
|
+
ProtocolConfigurationError,
|
|
83
|
+
RuntimeHostError,
|
|
84
|
+
)
|
|
85
|
+
from omnibase_infra.mixins import MixinEnvelopeExtraction
|
|
86
|
+
from omnibase_infra.observability.handlers.model_logging_handler_config import (
|
|
87
|
+
ModelLoggingHandlerConfig,
|
|
88
|
+
)
|
|
89
|
+
from omnibase_infra.observability.handlers.model_logging_handler_response import (
|
|
90
|
+
ModelLoggingHandlerResponse,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Dependency validation for structlog (via SinkLoggingStructured)
|
|
94
|
+
# The sink requires structlog; check availability here for early error detection
|
|
95
|
+
_STRUCTLOG_AVAILABLE: bool = False
|
|
96
|
+
try:
|
|
97
|
+
import structlog
|
|
98
|
+
|
|
99
|
+
_STRUCTLOG_AVAILABLE = True
|
|
100
|
+
del structlog # Not used at runtime, only for dependency check
|
|
101
|
+
except ImportError:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
# Import sink after dependency check (will fail at instantiation if structlog missing)
|
|
105
|
+
from omnibase_infra.observability.sinks import SinkLoggingStructured
|
|
106
|
+
|
|
107
|
+
if TYPE_CHECKING:
|
|
108
|
+
from omnibase_core.enums import EnumLogLevel
|
|
109
|
+
|
|
110
|
+
logger = logging.getLogger(__name__)
|
|
111
|
+
|
|
112
|
+
# Handler ID for responses
|
|
113
|
+
HANDLER_ID_LOGGING: str = "logging-handler"
|
|
114
|
+
|
|
115
|
+
# Supported operations
|
|
116
|
+
SUPPORTED_OPERATIONS: frozenset[str] = frozenset(
|
|
117
|
+
{
|
|
118
|
+
"logging.emit",
|
|
119
|
+
"logging.flush",
|
|
120
|
+
"logging.configure",
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class HandlerLoggingStructured(MixinEnvelopeExtraction):
|
|
126
|
+
"""Structured logging EFFECT handler with buffer/flush management.
|
|
127
|
+
|
|
128
|
+
This handler manages the lifecycle of a SinkLoggingStructured instance,
|
|
129
|
+
providing contract-driven initialization, periodic background flush,
|
|
130
|
+
and graceful shutdown with final flush.
|
|
131
|
+
|
|
132
|
+
Lifecycle:
|
|
133
|
+
1. initialize(): Creates sink, starts periodic flush task
|
|
134
|
+
2. execute(): Routes operations to sink methods
|
|
135
|
+
3. shutdown(): Cancels flush task, performs final flush
|
|
136
|
+
|
|
137
|
+
Buffer Management:
|
|
138
|
+
- The sink maintains a bounded buffer with configurable size
|
|
139
|
+
- Periodic flush: Background task flushes at configured interval
|
|
140
|
+
- Threshold flush: Automatic flush when buffer nears capacity
|
|
141
|
+
- Shutdown flush: Final flush ensures no data loss
|
|
142
|
+
|
|
143
|
+
Buffer Metrics:
|
|
144
|
+
The periodic flush loop emits the following metrics for monitoring:
|
|
145
|
+
- logging_buffer_utilization_ratio: Gauge (0.0-1.0) of buffer usage
|
|
146
|
+
- logging_buffer_dropped_total: Counter of entries dropped since last flush
|
|
147
|
+
|
|
148
|
+
These metrics help detect if buffer size is insufficient before logs are lost.
|
|
149
|
+
|
|
150
|
+
Thread Safety:
|
|
151
|
+
- Configuration changes use asyncio.Lock (prevents concurrent configure calls)
|
|
152
|
+
- Emit operations use threading.Lock (prevents log loss during reconfiguration)
|
|
153
|
+
- Sink operations use threading.Lock (internal to sink for buffer access)
|
|
154
|
+
- Safe for concurrent async callers; emit is protected during sink swap
|
|
155
|
+
|
|
156
|
+
Example:
|
|
157
|
+
```python
|
|
158
|
+
handler = HandlerLoggingStructured()
|
|
159
|
+
await handler.initialize({
|
|
160
|
+
"buffer_size": 500,
|
|
161
|
+
"flush_interval_seconds": 10.0,
|
|
162
|
+
"output_format": "json",
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
# Emit log entries
|
|
166
|
+
await handler.execute({
|
|
167
|
+
"operation": "logging.emit",
|
|
168
|
+
"payload": {
|
|
169
|
+
"level": "INFO",
|
|
170
|
+
"message": "User logged in",
|
|
171
|
+
"context": {"user_id": "u_123"},
|
|
172
|
+
},
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
# Force flush
|
|
176
|
+
await handler.execute({
|
|
177
|
+
"operation": "logging.flush",
|
|
178
|
+
"payload": {},
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
await handler.shutdown()
|
|
182
|
+
```
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
def __init__(self) -> None:
|
|
186
|
+
"""Initialize handler in uninitialized state."""
|
|
187
|
+
self._sink: SinkLoggingStructured | None = None
|
|
188
|
+
self._config: ModelLoggingHandlerConfig | None = None
|
|
189
|
+
self._initialized: bool = False
|
|
190
|
+
self._flush_task: asyncio.Task[None] | None = None
|
|
191
|
+
self._shutdown_event: asyncio.Event | None = None
|
|
192
|
+
self._config_lock: asyncio.Lock = asyncio.Lock()
|
|
193
|
+
# Threading lock to prevent log loss during reconfiguration.
|
|
194
|
+
# This lock ensures emit operations complete before sink swap+flush.
|
|
195
|
+
self._emit_lock: threading.Lock = threading.Lock()
|
|
196
|
+
# Track dropped count from previous flush cycle to emit delta
|
|
197
|
+
self._last_drop_count: int = 0
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def handler_type(self) -> EnumHandlerType:
|
|
201
|
+
"""Return the architectural role of this handler.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
EnumHandlerType.INFRA_HANDLER - This handler is an infrastructure
|
|
205
|
+
handler that manages the logging sink lifecycle. It is not a
|
|
206
|
+
NODE_HANDLER (event processing) or COMPUTE_HANDLER (pure computation).
|
|
207
|
+
|
|
208
|
+
Note:
|
|
209
|
+
handler_type determines lifecycle, protocol selection, and runtime
|
|
210
|
+
invocation patterns. It answers "what is this handler in the architecture?"
|
|
211
|
+
|
|
212
|
+
See Also:
|
|
213
|
+
- handler_category: Behavioral classification (EFFECT/COMPUTE)
|
|
214
|
+
- docs/architecture/HANDLER_PROTOCOL_DRIVEN_ARCHITECTURE.md
|
|
215
|
+
"""
|
|
216
|
+
return EnumHandlerType.INFRA_HANDLER
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def handler_category(self) -> EnumHandlerTypeCategory:
|
|
220
|
+
"""Return the behavioral classification of this handler.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
EnumHandlerTypeCategory.EFFECT - This handler performs side-effecting
|
|
224
|
+
I/O operations (writing logs to stdout/files). EFFECT handlers are
|
|
225
|
+
not deterministic and interact with external systems.
|
|
226
|
+
|
|
227
|
+
Note:
|
|
228
|
+
handler_category determines security rules, determinism guarantees,
|
|
229
|
+
replay safety, and permissions. It answers "how does this handler
|
|
230
|
+
behave at runtime?"
|
|
231
|
+
|
|
232
|
+
Categories:
|
|
233
|
+
- COMPUTE: Pure, deterministic transformations (no side effects)
|
|
234
|
+
- EFFECT: Side-effecting I/O (database, HTTP, file writes, logging)
|
|
235
|
+
- NONDETERMINISTIC_COMPUTE: Pure but not deterministic (UUID, random)
|
|
236
|
+
|
|
237
|
+
See Also:
|
|
238
|
+
- handler_type: Architectural role (INFRA_HANDLER/NODE_HANDLER/etc.)
|
|
239
|
+
- docs/architecture/HANDLER_PROTOCOL_DRIVEN_ARCHITECTURE.md
|
|
240
|
+
"""
|
|
241
|
+
return EnumHandlerTypeCategory.EFFECT
|
|
242
|
+
|
|
243
|
+
async def initialize(self, config: dict[str, object]) -> None:
|
|
244
|
+
"""Initialize the logging handler with configuration.
|
|
245
|
+
|
|
246
|
+
Creates the SinkLoggingStructured instance and starts the periodic
|
|
247
|
+
flush background task if configured.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
config: Configuration dict containing:
|
|
251
|
+
- buffer_size: Max buffer size (default: 1000)
|
|
252
|
+
- flush_interval_seconds: Flush interval (default: 5.0, 0 to disable)
|
|
253
|
+
- output_format: "json" or "console" (default: "json")
|
|
254
|
+
- drop_policy: "drop_oldest" (only supported policy)
|
|
255
|
+
|
|
256
|
+
Raises:
|
|
257
|
+
ProtocolConfigurationError: If structlog is not installed or
|
|
258
|
+
configuration validation fails.
|
|
259
|
+
RuntimeHostError: If handler is already initialized.
|
|
260
|
+
"""
|
|
261
|
+
init_correlation_id = uuid4()
|
|
262
|
+
|
|
263
|
+
# Validate required dependencies before proceeding
|
|
264
|
+
if not _STRUCTLOG_AVAILABLE:
|
|
265
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
266
|
+
correlation_id=init_correlation_id,
|
|
267
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
268
|
+
operation="initialize",
|
|
269
|
+
target_name="logging_handler",
|
|
270
|
+
)
|
|
271
|
+
raise ProtocolConfigurationError(
|
|
272
|
+
"Missing required dependency: structlog. "
|
|
273
|
+
"Install with: pip install structlog",
|
|
274
|
+
context=ctx,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
logger.info(
|
|
278
|
+
"Initializing %s",
|
|
279
|
+
self.__class__.__name__,
|
|
280
|
+
extra={
|
|
281
|
+
"handler": self.__class__.__name__,
|
|
282
|
+
"correlation_id": str(init_correlation_id),
|
|
283
|
+
},
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
if self._initialized:
|
|
287
|
+
# Log warning before raising to help users understand their config
|
|
288
|
+
# is not being applied (the handler maintains existing configuration)
|
|
289
|
+
logger.warning(
|
|
290
|
+
"Handler %s already initialized; ignoring new configuration. "
|
|
291
|
+
"Call shutdown() first to reinitialize with new config.",
|
|
292
|
+
self.__class__.__name__,
|
|
293
|
+
extra={
|
|
294
|
+
"handler": self.__class__.__name__,
|
|
295
|
+
"correlation_id": str(init_correlation_id),
|
|
296
|
+
"existing_config": self._config.model_dump()
|
|
297
|
+
if self._config
|
|
298
|
+
else None,
|
|
299
|
+
},
|
|
300
|
+
)
|
|
301
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
302
|
+
correlation_id=init_correlation_id,
|
|
303
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
304
|
+
operation="initialize",
|
|
305
|
+
target_name="logging_handler",
|
|
306
|
+
)
|
|
307
|
+
raise RuntimeHostError(
|
|
308
|
+
"Handler already initialized. Call shutdown() first.",
|
|
309
|
+
context=ctx,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Validate and parse configuration
|
|
313
|
+
# NOTE: Broad Exception catch is intentional here because Pydantic can raise
|
|
314
|
+
# various exception types (ValidationError, TypeError, ValueError) depending
|
|
315
|
+
# on the validation failure. We wrap all in ProtocolConfigurationError.
|
|
316
|
+
try:
|
|
317
|
+
self._config = ModelLoggingHandlerConfig.model_validate(config)
|
|
318
|
+
except Exception as e:
|
|
319
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
320
|
+
correlation_id=init_correlation_id,
|
|
321
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
322
|
+
operation="initialize",
|
|
323
|
+
target_name="logging_handler",
|
|
324
|
+
)
|
|
325
|
+
raise ProtocolConfigurationError(
|
|
326
|
+
f"Invalid logging handler configuration: {e}",
|
|
327
|
+
context=ctx,
|
|
328
|
+
) from e
|
|
329
|
+
|
|
330
|
+
# Create the sink with ALL config fields applied, including drop_policy.
|
|
331
|
+
# The drop_policy controls buffer overflow behavior (currently only "drop_oldest").
|
|
332
|
+
self._sink = SinkLoggingStructured(
|
|
333
|
+
max_buffer_size=self._config.buffer_size,
|
|
334
|
+
output_format=self._config.output_format,
|
|
335
|
+
drop_policy=self._config.drop_policy,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
# Reset drop count tracker for new sink
|
|
339
|
+
self._last_drop_count = 0
|
|
340
|
+
|
|
341
|
+
# Set up shutdown event and start periodic flush task
|
|
342
|
+
self._shutdown_event = asyncio.Event()
|
|
343
|
+
|
|
344
|
+
if self._config.flush_interval_seconds > 0:
|
|
345
|
+
self._flush_task = asyncio.create_task(
|
|
346
|
+
self._periodic_flush_loop(),
|
|
347
|
+
name="logging-handler-flush",
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
self._initialized = True
|
|
351
|
+
|
|
352
|
+
logger.info(
|
|
353
|
+
"%s initialized successfully",
|
|
354
|
+
self.__class__.__name__,
|
|
355
|
+
extra={
|
|
356
|
+
"handler": self.__class__.__name__,
|
|
357
|
+
"buffer_size": self._config.buffer_size,
|
|
358
|
+
"flush_interval_seconds": self._config.flush_interval_seconds,
|
|
359
|
+
"output_format": self._config.output_format,
|
|
360
|
+
"drop_policy": self._config.drop_policy,
|
|
361
|
+
"correlation_id": str(init_correlation_id),
|
|
362
|
+
},
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
async def shutdown(self) -> None:
|
|
366
|
+
"""Shutdown the logging handler gracefully.
|
|
367
|
+
|
|
368
|
+
Cancels the periodic flush task, performs a final flush of all
|
|
369
|
+
buffered entries, and releases resources.
|
|
370
|
+
|
|
371
|
+
This method is idempotent - calling it multiple times is safe.
|
|
372
|
+
"""
|
|
373
|
+
shutdown_correlation_id = uuid4()
|
|
374
|
+
|
|
375
|
+
logger.info(
|
|
376
|
+
"Shutting down %s",
|
|
377
|
+
self.__class__.__name__,
|
|
378
|
+
extra={
|
|
379
|
+
"handler": self.__class__.__name__,
|
|
380
|
+
"correlation_id": str(shutdown_correlation_id),
|
|
381
|
+
},
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# Signal shutdown to flush loop
|
|
385
|
+
if self._shutdown_event is not None:
|
|
386
|
+
self._shutdown_event.set()
|
|
387
|
+
|
|
388
|
+
# Cancel periodic flush task
|
|
389
|
+
if self._flush_task is not None:
|
|
390
|
+
self._flush_task.cancel()
|
|
391
|
+
try:
|
|
392
|
+
await self._flush_task
|
|
393
|
+
except asyncio.CancelledError:
|
|
394
|
+
pass
|
|
395
|
+
self._flush_task = None
|
|
396
|
+
|
|
397
|
+
# Final flush - best effort, never fails shutdown
|
|
398
|
+
# NOTE: Broad Exception catch is intentional here to ensure shutdown
|
|
399
|
+
# always completes regardless of I/O errors. Any flush error during
|
|
400
|
+
# shutdown is logged but does not prevent cleanup from completing.
|
|
401
|
+
if self._sink is not None:
|
|
402
|
+
try:
|
|
403
|
+
self._sink.flush()
|
|
404
|
+
except Exception as e:
|
|
405
|
+
logger.warning(
|
|
406
|
+
"Error during final flush on shutdown: %s",
|
|
407
|
+
e,
|
|
408
|
+
extra={
|
|
409
|
+
"handler": self.__class__.__name__,
|
|
410
|
+
"correlation_id": str(shutdown_correlation_id),
|
|
411
|
+
"error_type": type(e).__name__,
|
|
412
|
+
},
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
# Clear state
|
|
416
|
+
self._sink = None
|
|
417
|
+
self._config = None
|
|
418
|
+
self._initialized = False
|
|
419
|
+
self._shutdown_event = None
|
|
420
|
+
|
|
421
|
+
logger.info(
|
|
422
|
+
"%s shutdown complete",
|
|
423
|
+
self.__class__.__name__,
|
|
424
|
+
extra={
|
|
425
|
+
"handler": self.__class__.__name__,
|
|
426
|
+
"correlation_id": str(shutdown_correlation_id),
|
|
427
|
+
},
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
async def execute(self, envelope: dict[str, object]) -> ModelLoggingHandlerResponse:
|
|
431
|
+
"""Execute a logging operation from the envelope.
|
|
432
|
+
|
|
433
|
+
Supported operations:
|
|
434
|
+
- logging.emit: Buffer a log entry
|
|
435
|
+
- logging.flush: Force flush all buffered entries
|
|
436
|
+
- logging.configure: Update handler configuration
|
|
437
|
+
|
|
438
|
+
Args:
|
|
439
|
+
envelope: Request envelope containing:
|
|
440
|
+
- operation: Operation identifier (logging.emit, etc.)
|
|
441
|
+
- payload: Operation-specific payload
|
|
442
|
+
- correlation_id: Optional correlation ID for tracing
|
|
443
|
+
- envelope_id: Optional envelope ID for causality tracking
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
ModelLoggingHandlerResponse with operation result
|
|
447
|
+
|
|
448
|
+
Raises:
|
|
449
|
+
RuntimeHostError: If handler not initialized or invalid operation.
|
|
450
|
+
"""
|
|
451
|
+
correlation_id = self._extract_correlation_id(envelope)
|
|
452
|
+
|
|
453
|
+
if not self._initialized or self._sink is None or self._config is None:
|
|
454
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
455
|
+
correlation_id=correlation_id,
|
|
456
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
457
|
+
operation="execute",
|
|
458
|
+
target_name="logging_handler",
|
|
459
|
+
)
|
|
460
|
+
raise RuntimeHostError(
|
|
461
|
+
"Logging handler not initialized. Call initialize() first.",
|
|
462
|
+
context=ctx,
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
operation = envelope.get("operation")
|
|
466
|
+
if not isinstance(operation, str):
|
|
467
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
468
|
+
correlation_id=correlation_id,
|
|
469
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
470
|
+
operation="execute",
|
|
471
|
+
target_name="logging_handler",
|
|
472
|
+
)
|
|
473
|
+
raise RuntimeHostError(
|
|
474
|
+
"Missing or invalid 'operation' in envelope",
|
|
475
|
+
context=ctx,
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
if operation not in SUPPORTED_OPERATIONS:
|
|
479
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
480
|
+
correlation_id=correlation_id,
|
|
481
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
482
|
+
operation=operation,
|
|
483
|
+
target_name="logging_handler",
|
|
484
|
+
)
|
|
485
|
+
raise RuntimeHostError(
|
|
486
|
+
f"Operation '{operation}' not supported. "
|
|
487
|
+
f"Available: {', '.join(sorted(SUPPORTED_OPERATIONS))}",
|
|
488
|
+
context=ctx,
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
payload = envelope.get("payload")
|
|
492
|
+
if not isinstance(payload, dict):
|
|
493
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
494
|
+
correlation_id=correlation_id,
|
|
495
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
496
|
+
operation=operation,
|
|
497
|
+
target_name="logging_handler",
|
|
498
|
+
)
|
|
499
|
+
raise RuntimeHostError(
|
|
500
|
+
"Missing or invalid 'payload' in envelope",
|
|
501
|
+
context=ctx,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
# Route to operation handler
|
|
505
|
+
if operation == "logging.emit":
|
|
506
|
+
return self._handle_emit(payload, correlation_id)
|
|
507
|
+
elif operation == "logging.flush":
|
|
508
|
+
return self._handle_flush(correlation_id)
|
|
509
|
+
else: # logging.configure
|
|
510
|
+
return await self._handle_configure(payload, correlation_id)
|
|
511
|
+
|
|
512
|
+
def _handle_emit(
|
|
513
|
+
self,
|
|
514
|
+
payload: dict[str, object],
|
|
515
|
+
correlation_id: UUID,
|
|
516
|
+
) -> ModelLoggingHandlerResponse:
|
|
517
|
+
"""Handle logging.emit operation.
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
payload: Payload containing:
|
|
521
|
+
- level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL, etc.)
|
|
522
|
+
- message: Log message string
|
|
523
|
+
- context: Dict of string key-value context data
|
|
524
|
+
correlation_id: Correlation ID for tracing
|
|
525
|
+
|
|
526
|
+
Returns:
|
|
527
|
+
ModelLoggingHandlerResponse with operation result
|
|
528
|
+
|
|
529
|
+
Thread Safety:
|
|
530
|
+
This method acquires _emit_lock to prevent log loss during
|
|
531
|
+
reconfiguration. The lock ensures the sink reference is stable
|
|
532
|
+
throughout the emit operation.
|
|
533
|
+
"""
|
|
534
|
+
# Extract and validate payload fields BEFORE acquiring lock
|
|
535
|
+
# to minimize time spent holding the lock
|
|
536
|
+
level_raw = payload.get("level")
|
|
537
|
+
message = payload.get("message")
|
|
538
|
+
context_raw = payload.get("context", {})
|
|
539
|
+
|
|
540
|
+
if not isinstance(message, str):
|
|
541
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
542
|
+
correlation_id=correlation_id,
|
|
543
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
544
|
+
operation="logging.emit",
|
|
545
|
+
target_name="logging_handler",
|
|
546
|
+
)
|
|
547
|
+
raise RuntimeHostError(
|
|
548
|
+
"Missing or invalid 'message' in payload - must be string",
|
|
549
|
+
context=ctx,
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
if not isinstance(context_raw, dict):
|
|
553
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
554
|
+
correlation_id=correlation_id,
|
|
555
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
556
|
+
operation="logging.emit",
|
|
557
|
+
target_name="logging_handler",
|
|
558
|
+
)
|
|
559
|
+
raise RuntimeHostError(
|
|
560
|
+
"Invalid 'context' in payload - must be dict",
|
|
561
|
+
context=ctx,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
# Pass through JSON-compatible context values (sink accepts JsonType)
|
|
565
|
+
# This preserves rich context data (int, float, bool, list, dict) for structured logging
|
|
566
|
+
context: dict[str, JsonType] = {str(k): v for k, v in context_raw.items()}
|
|
567
|
+
|
|
568
|
+
# Parse log level
|
|
569
|
+
level = self._parse_log_level(level_raw, correlation_id)
|
|
570
|
+
|
|
571
|
+
# Critical section: acquire emit lock to prevent log loss during reconfiguration.
|
|
572
|
+
# This ensures the sink reference is stable and any logs emitted here will be
|
|
573
|
+
# flushed before the old sink is abandoned.
|
|
574
|
+
with self._emit_lock:
|
|
575
|
+
if self._sink is None:
|
|
576
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
577
|
+
correlation_id=correlation_id,
|
|
578
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
579
|
+
operation="logging.emit",
|
|
580
|
+
target_name="logging_handler",
|
|
581
|
+
)
|
|
582
|
+
raise RuntimeHostError(
|
|
583
|
+
"Sink not initialized",
|
|
584
|
+
context=ctx,
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
# Emit to sink (synchronous, non-blocking)
|
|
588
|
+
self._sink.emit(level, message, context)
|
|
589
|
+
|
|
590
|
+
# Capture metrics while still holding the lock for consistency
|
|
591
|
+
buffer_size = self._sink.buffer_size
|
|
592
|
+
drop_count = self._sink.drop_count
|
|
593
|
+
|
|
594
|
+
return ModelLoggingHandlerResponse(
|
|
595
|
+
status=EnumResponseStatus.SUCCESS,
|
|
596
|
+
operation="logging.emit",
|
|
597
|
+
message="Log entry buffered",
|
|
598
|
+
correlation_id=correlation_id,
|
|
599
|
+
buffer_size=buffer_size,
|
|
600
|
+
drop_count=drop_count,
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
def _handle_flush(
|
|
604
|
+
self,
|
|
605
|
+
correlation_id: UUID,
|
|
606
|
+
) -> ModelLoggingHandlerResponse:
|
|
607
|
+
"""Handle logging.flush operation.
|
|
608
|
+
|
|
609
|
+
Args:
|
|
610
|
+
correlation_id: Correlation ID for tracing
|
|
611
|
+
|
|
612
|
+
Returns:
|
|
613
|
+
ModelLoggingHandlerResponse with operation result
|
|
614
|
+
"""
|
|
615
|
+
if self._sink is None:
|
|
616
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
617
|
+
correlation_id=correlation_id,
|
|
618
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
619
|
+
operation="logging.flush",
|
|
620
|
+
target_name="logging_handler",
|
|
621
|
+
)
|
|
622
|
+
raise RuntimeHostError(
|
|
623
|
+
"Sink not initialized",
|
|
624
|
+
context=ctx,
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
# Capture pre-flush metrics
|
|
628
|
+
pre_buffer_size = self._sink.buffer_size
|
|
629
|
+
|
|
630
|
+
# Flush (synchronous I/O)
|
|
631
|
+
self._sink.flush()
|
|
632
|
+
|
|
633
|
+
return ModelLoggingHandlerResponse(
|
|
634
|
+
status=EnumResponseStatus.SUCCESS,
|
|
635
|
+
operation="logging.flush",
|
|
636
|
+
message=f"Flushed {pre_buffer_size} entries",
|
|
637
|
+
correlation_id=correlation_id,
|
|
638
|
+
buffer_size=self._sink.buffer_size,
|
|
639
|
+
drop_count=self._sink.drop_count,
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
async def _handle_configure(
|
|
643
|
+
self,
|
|
644
|
+
payload: dict[str, object],
|
|
645
|
+
correlation_id: UUID,
|
|
646
|
+
) -> ModelLoggingHandlerResponse:
|
|
647
|
+
"""Handle logging.configure operation.
|
|
648
|
+
|
|
649
|
+
Note: Configuration changes require re-creating the sink. To prevent
|
|
650
|
+
log loss, the swap-then-flush sequence ensures the atomic swap happens
|
|
651
|
+
first under _emit_lock, then the old sink is flushed after releasing
|
|
652
|
+
the lock.
|
|
653
|
+
|
|
654
|
+
Thread Safety:
|
|
655
|
+
The _emit_lock is acquired during the atomic swap operation only.
|
|
656
|
+
This ensures that:
|
|
657
|
+
1. All in-flight emits complete before we start the swap
|
|
658
|
+
2. No new emits can start during the swap
|
|
659
|
+
3. After swap completes, new emits immediately go to the new sink
|
|
660
|
+
4. Old sink is flushed after the lock is released (no blocking I/O under lock)
|
|
661
|
+
|
|
662
|
+
Reconfiguration Sequence:
|
|
663
|
+
1. Acquire _emit_lock
|
|
664
|
+
2. Swap to new sink atomically (under lock)
|
|
665
|
+
3. Update config reference (under lock)
|
|
666
|
+
4. Release _emit_lock
|
|
667
|
+
5. Flush old sink (outside lock - logs during flush go to new sink)
|
|
668
|
+
This order guarantees no log loss and minimizes lock hold time.
|
|
669
|
+
|
|
670
|
+
Args:
|
|
671
|
+
payload: Configuration payload (same fields as ModelLoggingHandlerConfig)
|
|
672
|
+
correlation_id: Correlation ID for tracing
|
|
673
|
+
|
|
674
|
+
Returns:
|
|
675
|
+
ModelLoggingHandlerResponse with operation result
|
|
676
|
+
"""
|
|
677
|
+
async with self._config_lock:
|
|
678
|
+
if self._sink is None or self._config is None:
|
|
679
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
680
|
+
correlation_id=correlation_id,
|
|
681
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
682
|
+
operation="logging.configure",
|
|
683
|
+
target_name="logging_handler",
|
|
684
|
+
)
|
|
685
|
+
raise RuntimeHostError(
|
|
686
|
+
"Handler not initialized",
|
|
687
|
+
context=ctx,
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
# Merge existing config with new values
|
|
691
|
+
current_dict = self._config.model_dump()
|
|
692
|
+
current_dict.update(payload)
|
|
693
|
+
|
|
694
|
+
# Validate new configuration BEFORE acquiring emit lock
|
|
695
|
+
# NOTE: Broad Exception catch is intentional here because Pydantic can raise
|
|
696
|
+
# various exception types (ValidationError, TypeError, ValueError) depending
|
|
697
|
+
# on the validation failure. We wrap all in ProtocolConfigurationError.
|
|
698
|
+
try:
|
|
699
|
+
new_config = ModelLoggingHandlerConfig.model_validate(current_dict)
|
|
700
|
+
except Exception as e:
|
|
701
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
702
|
+
correlation_id=correlation_id,
|
|
703
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
704
|
+
operation="logging.configure",
|
|
705
|
+
target_name="logging_handler",
|
|
706
|
+
)
|
|
707
|
+
raise ProtocolConfigurationError(
|
|
708
|
+
f"Invalid configuration: {e}",
|
|
709
|
+
context=ctx,
|
|
710
|
+
) from e
|
|
711
|
+
|
|
712
|
+
# Create new sink with updated config BEFORE acquiring emit lock
|
|
713
|
+
# to minimize time spent blocking emit operations.
|
|
714
|
+
# All config fields are applied including drop_policy.
|
|
715
|
+
new_sink = SinkLoggingStructured(
|
|
716
|
+
max_buffer_size=new_config.buffer_size,
|
|
717
|
+
output_format=new_config.output_format,
|
|
718
|
+
drop_policy=new_config.drop_policy,
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
# Critical section: acquire emit lock for atomic swap-then-flush.
|
|
722
|
+
# This prevents the race condition where:
|
|
723
|
+
# 1. emit reads self._sink -> gets old_sink
|
|
724
|
+
# 2. configure swaps to new_sink
|
|
725
|
+
# 3. emit calls old_sink.emit() -> log LOST (old sink abandoned)
|
|
726
|
+
#
|
|
727
|
+
# By swapping BEFORE flush and flushing AFTER releasing the lock:
|
|
728
|
+
# - New emits immediately go to the new sink (no blocking during flush)
|
|
729
|
+
# - Old sink's buffer is preserved until explicitly flushed
|
|
730
|
+
# - Lock hold time is minimized (fast swap, no I/O under lock)
|
|
731
|
+
with self._emit_lock:
|
|
732
|
+
# Step 1: Capture old sink reference and swap atomically
|
|
733
|
+
old_sink = self._sink
|
|
734
|
+
self._sink = new_sink
|
|
735
|
+
self._config = new_config
|
|
736
|
+
|
|
737
|
+
# Step 2: Reset drop count tracker for new sink
|
|
738
|
+
self._last_drop_count = 0
|
|
739
|
+
|
|
740
|
+
# Step 3: Flush old sink AFTER releasing lock.
|
|
741
|
+
# Logs emitted during this flush go to the new sink (not lost).
|
|
742
|
+
# This is safe because old_sink's buffer is independent.
|
|
743
|
+
if old_sink is not None:
|
|
744
|
+
old_sink.flush()
|
|
745
|
+
|
|
746
|
+
# Handle flush interval changes (outside emit lock - these are async)
|
|
747
|
+
if self._config.flush_interval_seconds > 0:
|
|
748
|
+
# Restart flush task if not running or interval changed
|
|
749
|
+
if self._flush_task is None or self._flush_task.done():
|
|
750
|
+
self._flush_task = asyncio.create_task(
|
|
751
|
+
self._periodic_flush_loop(),
|
|
752
|
+
name="logging-handler-flush",
|
|
753
|
+
)
|
|
754
|
+
# Cancel flush task if interval is 0
|
|
755
|
+
elif self._flush_task is not None and not self._flush_task.done():
|
|
756
|
+
self._flush_task.cancel()
|
|
757
|
+
try:
|
|
758
|
+
await self._flush_task
|
|
759
|
+
except asyncio.CancelledError:
|
|
760
|
+
pass
|
|
761
|
+
self._flush_task = None
|
|
762
|
+
|
|
763
|
+
return ModelLoggingHandlerResponse(
|
|
764
|
+
status=EnumResponseStatus.SUCCESS,
|
|
765
|
+
operation="logging.configure",
|
|
766
|
+
message="Configuration updated",
|
|
767
|
+
correlation_id=correlation_id,
|
|
768
|
+
buffer_size=self._sink.buffer_size if self._sink else 0,
|
|
769
|
+
drop_count=self._sink.drop_count if self._sink else 0,
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
def _parse_log_level(
|
|
773
|
+
self,
|
|
774
|
+
level_raw: object,
|
|
775
|
+
correlation_id: UUID,
|
|
776
|
+
) -> EnumLogLevel:
|
|
777
|
+
"""Parse log level from payload.
|
|
778
|
+
|
|
779
|
+
Accepts EnumLogLevel enum values or string level names.
|
|
780
|
+
|
|
781
|
+
Args:
|
|
782
|
+
level_raw: Raw level value from payload
|
|
783
|
+
correlation_id: Correlation ID for error context
|
|
784
|
+
|
|
785
|
+
Returns:
|
|
786
|
+
Parsed EnumLogLevel
|
|
787
|
+
|
|
788
|
+
Raises:
|
|
789
|
+
RuntimeHostError: If level is invalid
|
|
790
|
+
"""
|
|
791
|
+
# Lazy import to avoid circular dependency
|
|
792
|
+
from omnibase_core.enums import EnumLogLevel
|
|
793
|
+
|
|
794
|
+
if isinstance(level_raw, EnumLogLevel):
|
|
795
|
+
return level_raw
|
|
796
|
+
|
|
797
|
+
if isinstance(level_raw, str):
|
|
798
|
+
# Try to match by value (case-insensitive)
|
|
799
|
+
level_upper = level_raw.upper()
|
|
800
|
+
for log_level in EnumLogLevel:
|
|
801
|
+
if log_level.value.upper() == level_upper:
|
|
802
|
+
return log_level
|
|
803
|
+
if log_level.name.upper() == level_upper:
|
|
804
|
+
return log_level
|
|
805
|
+
|
|
806
|
+
# Default to INFO for unknown levels
|
|
807
|
+
if level_raw is None:
|
|
808
|
+
return EnumLogLevel.INFO
|
|
809
|
+
|
|
810
|
+
ctx = ModelInfraErrorContext.with_correlation(
|
|
811
|
+
correlation_id=correlation_id,
|
|
812
|
+
transport_type=EnumInfraTransportType.RUNTIME,
|
|
813
|
+
operation="logging.emit",
|
|
814
|
+
target_name="logging_handler",
|
|
815
|
+
)
|
|
816
|
+
raise RuntimeHostError(
|
|
817
|
+
f"Invalid log level: {level_raw!r}. "
|
|
818
|
+
f"Valid levels: {[lvl.value for lvl in EnumLogLevel]}",
|
|
819
|
+
context=ctx,
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
async def _periodic_flush_loop(self) -> None:
|
|
823
|
+
"""Background task for periodic buffer flush.
|
|
824
|
+
|
|
825
|
+
Runs until shutdown_event is set or task is cancelled.
|
|
826
|
+
Handles flush errors gracefully to prevent task crash.
|
|
827
|
+
|
|
828
|
+
Emits buffer utilization metrics before each flush:
|
|
829
|
+
- logging_buffer_utilization_ratio: Gauge showing buffer usage (0.0-1.0)
|
|
830
|
+
- logging_buffer_dropped_total: Counter of entries dropped since last flush
|
|
831
|
+
"""
|
|
832
|
+
if self._shutdown_event is None:
|
|
833
|
+
return
|
|
834
|
+
|
|
835
|
+
while not self._shutdown_event.is_set():
|
|
836
|
+
try:
|
|
837
|
+
# Wait for flush interval or shutdown signal
|
|
838
|
+
flush_interval = (
|
|
839
|
+
self._config.flush_interval_seconds
|
|
840
|
+
if self._config is not None
|
|
841
|
+
else 5.0
|
|
842
|
+
)
|
|
843
|
+
try:
|
|
844
|
+
await asyncio.wait_for(
|
|
845
|
+
self._shutdown_event.wait(),
|
|
846
|
+
timeout=flush_interval,
|
|
847
|
+
)
|
|
848
|
+
# Shutdown signaled
|
|
849
|
+
break
|
|
850
|
+
except TimeoutError:
|
|
851
|
+
# Normal timeout - time to flush
|
|
852
|
+
pass
|
|
853
|
+
|
|
854
|
+
# Perform flush with metrics emission
|
|
855
|
+
if self._sink is not None:
|
|
856
|
+
# Emit buffer utilization metric (gauge: 0.0 to 1.0)
|
|
857
|
+
# Safe from division by zero: max_buffer_size validated >= 1 at init
|
|
858
|
+
max_size = self._sink.max_buffer_size
|
|
859
|
+
current_size = self._sink.buffer_size
|
|
860
|
+
utilization_ratio = current_size / max_size if max_size > 0 else 0.0
|
|
861
|
+
|
|
862
|
+
logger.debug(
|
|
863
|
+
"logging_buffer_utilization_ratio",
|
|
864
|
+
extra={
|
|
865
|
+
"handler": self.__class__.__name__,
|
|
866
|
+
"metric_type": "gauge",
|
|
867
|
+
"metric_name": "logging_buffer_utilization_ratio",
|
|
868
|
+
"value": utilization_ratio,
|
|
869
|
+
"buffer_size": current_size,
|
|
870
|
+
"max_buffer_size": max_size,
|
|
871
|
+
},
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
# Emit dropped logs counter (delta since last flush)
|
|
875
|
+
current_drop_count = self._sink.drop_count
|
|
876
|
+
dropped_since_last = current_drop_count - self._last_drop_count
|
|
877
|
+
if dropped_since_last > 0:
|
|
878
|
+
logger.warning(
|
|
879
|
+
"logging_buffer_dropped_total",
|
|
880
|
+
extra={
|
|
881
|
+
"handler": self.__class__.__name__,
|
|
882
|
+
"metric_type": "counter",
|
|
883
|
+
"metric_name": "logging_buffer_dropped_total",
|
|
884
|
+
"value": dropped_since_last,
|
|
885
|
+
"total_dropped": current_drop_count,
|
|
886
|
+
},
|
|
887
|
+
)
|
|
888
|
+
self._last_drop_count = current_drop_count
|
|
889
|
+
|
|
890
|
+
# Perform flush
|
|
891
|
+
self._sink.flush()
|
|
892
|
+
|
|
893
|
+
except asyncio.CancelledError:
|
|
894
|
+
# Task cancelled - exit gracefully
|
|
895
|
+
break
|
|
896
|
+
except Exception as e:
|
|
897
|
+
# NOTE: Broad Exception catch is intentional here to keep the
|
|
898
|
+
# background flush loop running despite transient I/O errors.
|
|
899
|
+
# This ensures the handler remains operational even if individual
|
|
900
|
+
# flush operations fail temporarily. The sleep prevents tight loops.
|
|
901
|
+
logger.warning(
|
|
902
|
+
"Error in periodic flush loop: %s",
|
|
903
|
+
e,
|
|
904
|
+
extra={
|
|
905
|
+
"handler": self.__class__.__name__,
|
|
906
|
+
"error_type": type(e).__name__,
|
|
907
|
+
},
|
|
908
|
+
)
|
|
909
|
+
# Brief sleep to prevent tight error loop on persistent failures
|
|
910
|
+
await asyncio.sleep(1.0)
|
|
911
|
+
|
|
912
|
+
def describe(self) -> dict[str, object]:
|
|
913
|
+
"""Return handler metadata and capabilities for introspection.
|
|
914
|
+
|
|
915
|
+
This method exposes the handler's type classification along with its
|
|
916
|
+
operational configuration and capabilities.
|
|
917
|
+
|
|
918
|
+
Returns:
|
|
919
|
+
dict containing:
|
|
920
|
+
- handler_type: Architectural role (infra_handler)
|
|
921
|
+
- handler_category: Behavioral classification (effect)
|
|
922
|
+
- supported_operations: List of supported operations
|
|
923
|
+
- initialized: Whether the handler is initialized
|
|
924
|
+
- version: Handler version string
|
|
925
|
+
- config: Current configuration (if initialized)
|
|
926
|
+
- buffer_size: Current buffer size (if initialized)
|
|
927
|
+
- max_buffer_size: Maximum buffer capacity (if initialized)
|
|
928
|
+
- drop_count: Total dropped entries (if initialized)
|
|
929
|
+
- buffer_utilization_ratio: Current utilization (0.0-1.0)
|
|
930
|
+
|
|
931
|
+
See Also:
|
|
932
|
+
- handler_type property: Full documentation of architectural role
|
|
933
|
+
- handler_category property: Full documentation of behavioral classification
|
|
934
|
+
"""
|
|
935
|
+
result: dict[str, object] = {
|
|
936
|
+
"handler_type": self.handler_type.value,
|
|
937
|
+
"handler_category": self.handler_category.value,
|
|
938
|
+
"supported_operations": sorted(SUPPORTED_OPERATIONS),
|
|
939
|
+
"initialized": self._initialized,
|
|
940
|
+
"version": "0.1.0-mvp",
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
if self._initialized and self._config is not None:
|
|
944
|
+
result["config"] = {
|
|
945
|
+
"buffer_size": self._config.buffer_size,
|
|
946
|
+
"flush_interval_seconds": self._config.flush_interval_seconds,
|
|
947
|
+
"output_format": self._config.output_format,
|
|
948
|
+
"drop_policy": self._config.drop_policy,
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
if self._sink is not None:
|
|
952
|
+
result["buffer_size"] = self._sink.buffer_size
|
|
953
|
+
result["max_buffer_size"] = self._sink.max_buffer_size
|
|
954
|
+
result["drop_count"] = self._sink.drop_count
|
|
955
|
+
# Include utilization ratio for monitoring
|
|
956
|
+
max_size = self._sink.max_buffer_size
|
|
957
|
+
result["buffer_utilization_ratio"] = (
|
|
958
|
+
self._sink.buffer_size / max_size if max_size > 0 else 0.0
|
|
959
|
+
)
|
|
960
|
+
|
|
961
|
+
return result
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
__all__: list[str] = [
|
|
965
|
+
"HandlerLoggingStructured",
|
|
966
|
+
"SUPPORTED_OPERATIONS",
|
|
967
|
+
]
|