tasker-engine 1.0.0
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.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +443 -0
- data/Rakefile +10 -0
- data/app/controllers/tasker/analytics_controller.rb +179 -0
- data/app/controllers/tasker/application_controller.rb +45 -0
- data/app/controllers/tasker/graphql_controller.rb +193 -0
- data/app/controllers/tasker/handlers_controller.rb +217 -0
- data/app/controllers/tasker/health_controller.rb +229 -0
- data/app/controllers/tasker/metrics_controller.rb +111 -0
- data/app/controllers/tasker/page_sort.rb +97 -0
- data/app/controllers/tasker/task_diagrams_controller.rb +30 -0
- data/app/controllers/tasker/tasks_controller.rb +123 -0
- data/app/controllers/tasker/workflow_steps_controller.rb +69 -0
- data/app/graphql/examples/all_tasks.graphql +22 -0
- data/app/graphql/examples/pending_tasks.graphql +23 -0
- data/app/graphql/tasker/graph_ql_types/annotation_type.rb +14 -0
- data/app/graphql/tasker/graph_ql_types/base_argument.rb +9 -0
- data/app/graphql/tasker/graph_ql_types/base_connection.rb +11 -0
- data/app/graphql/tasker/graph_ql_types/base_edge.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_enum.rb +9 -0
- data/app/graphql/tasker/graph_ql_types/base_field.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_input_object.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_interface.rb +14 -0
- data/app/graphql/tasker/graph_ql_types/base_object.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_scalar.rb +9 -0
- data/app/graphql/tasker/graph_ql_types/base_union.rb +11 -0
- data/app/graphql/tasker/graph_ql_types/dependent_system_object_map_type.rb +18 -0
- data/app/graphql/tasker/graph_ql_types/dependent_system_type.rb +13 -0
- data/app/graphql/tasker/graph_ql_types/mutation_type.rb +16 -0
- data/app/graphql/tasker/graph_ql_types/named_step_type.rb +16 -0
- data/app/graphql/tasker/graph_ql_types/named_task_type.rb +14 -0
- data/app/graphql/tasker/graph_ql_types/named_tasks_named_step_type.rb +19 -0
- data/app/graphql/tasker/graph_ql_types/node_type.rb +12 -0
- data/app/graphql/tasker/graph_ql_types/query_type.rb +20 -0
- data/app/graphql/tasker/graph_ql_types/task_annotation_type.rb +17 -0
- data/app/graphql/tasker/graph_ql_types/task_interface.rb +17 -0
- data/app/graphql/tasker/graph_ql_types/task_type.rb +26 -0
- data/app/graphql/tasker/graph_ql_types/workflow_step_type.rb +154 -0
- data/app/graphql/tasker/graph_ql_types.rb +42 -0
- data/app/graphql/tasker/mutations/base_mutation.rb +13 -0
- data/app/graphql/tasker/mutations/cancel_step.rb +29 -0
- data/app/graphql/tasker/mutations/cancel_task.rb +29 -0
- data/app/graphql/tasker/mutations/create_task.rb +52 -0
- data/app/graphql/tasker/mutations/update_step.rb +36 -0
- data/app/graphql/tasker/mutations/update_task.rb +41 -0
- data/app/graphql/tasker/queries/all_annotation_types.rb +17 -0
- data/app/graphql/tasker/queries/all_tasks.rb +23 -0
- data/app/graphql/tasker/queries/base_query.rb +9 -0
- data/app/graphql/tasker/queries/helpers.rb +16 -0
- data/app/graphql/tasker/queries/one_step.rb +24 -0
- data/app/graphql/tasker/queries/one_task.rb +18 -0
- data/app/graphql/tasker/queries/tasks_by_annotation.rb +31 -0
- data/app/graphql/tasker/queries/tasks_by_status.rb +30 -0
- data/app/graphql/tasker/tasker_rails_schema.rb +52 -0
- data/app/jobs/tasker/application_job.rb +8 -0
- data/app/jobs/tasker/metrics_export_job.rb +252 -0
- data/app/jobs/tasker/task_runner_job.rb +224 -0
- data/app/models/tasker/annotation_type.rb +26 -0
- data/app/models/tasker/application_record.rb +70 -0
- data/app/models/tasker/dependent_system.rb +26 -0
- data/app/models/tasker/dependent_system_object_map.rb +64 -0
- data/app/models/tasker/diagram/edge.rb +106 -0
- data/app/models/tasker/diagram/flowchart.rb +137 -0
- data/app/models/tasker/diagram/node.rb +99 -0
- data/app/models/tasker/named_step.rb +41 -0
- data/app/models/tasker/named_task.rb +121 -0
- data/app/models/tasker/named_tasks_named_step.rb +82 -0
- data/app/models/tasker/step_dag_relationship.rb +65 -0
- data/app/models/tasker/step_readiness_status.rb +59 -0
- data/app/models/tasker/task.rb +424 -0
- data/app/models/tasker/task_annotation.rb +36 -0
- data/app/models/tasker/task_diagram.rb +332 -0
- data/app/models/tasker/task_execution_context.rb +29 -0
- data/app/models/tasker/task_namespace.rb +41 -0
- data/app/models/tasker/task_transition.rb +235 -0
- data/app/models/tasker/workflow_step.rb +461 -0
- data/app/models/tasker/workflow_step_edge.rb +94 -0
- data/app/models/tasker/workflow_step_transition.rb +434 -0
- data/app/serializers/tasker/annotation_type_serializer.rb +8 -0
- data/app/serializers/tasker/handler_serializer.rb +109 -0
- data/app/serializers/tasker/task_annotation_serializer.rb +32 -0
- data/app/serializers/tasker/task_serializer.rb +168 -0
- data/app/serializers/tasker/workflow_step_serializer.rb +27 -0
- data/app/services/tasker/analytics_service.rb +409 -0
- data/app/views/tasker/task/_diagram.html.erb +32 -0
- data/config/initializers/dry_struct.rb +11 -0
- data/config/initializers/statesman.rb +6 -0
- data/config/initializers/tasker_orchestration.rb +17 -0
- data/config/initializers/time_formats.rb +4 -0
- data/config/routes.rb +34 -0
- data/config/tasker/subscriptions/example_integrations.yml +67 -0
- data/config/tasker/system_events.yml +305 -0
- data/db/functions/calculate_dependency_levels_v01.sql +45 -0
- data/db/functions/get_analytics_metrics_v01.sql +137 -0
- data/db/functions/get_slowest_steps_v01.sql +82 -0
- data/db/functions/get_slowest_tasks_v01.sql +96 -0
- data/db/functions/get_step_readiness_status_batch_v01.sql +140 -0
- data/db/functions/get_step_readiness_status_v01.sql +139 -0
- data/db/functions/get_system_health_counts_v01.sql +108 -0
- data/db/functions/get_task_execution_context_v01.sql +108 -0
- data/db/functions/get_task_execution_contexts_batch_v01.sql +104 -0
- data/db/init/schema.sql +2277 -0
- data/db/migrate/20250701165431_initial_tasker_schema.rb +116 -0
- data/db/views/tasker_step_dag_relationships_v01.sql +69 -0
- data/docs/APPLICATION_GENERATOR.md +384 -0
- data/docs/AUTH.md +1780 -0
- data/docs/CIRCUIT_BREAKER.md +224 -0
- data/docs/DEVELOPER_GUIDE.md +2665 -0
- data/docs/EVENT_SYSTEM.md +637 -0
- data/docs/EXECUTION_CONFIGURATION.md +341 -0
- data/docs/FLOW_CHART.md +149 -0
- data/docs/HEALTH.md +542 -0
- data/docs/METRICS.md +731 -0
- data/docs/OPTIMIZATION_PLAN.md +1479 -0
- data/docs/OVERVIEW.md +552 -0
- data/docs/QUICK_START.md +270 -0
- data/docs/REGISTRY_SYSTEMS.md +373 -0
- data/docs/REST_API.md +632 -0
- data/docs/ROADMAP.md +221 -0
- data/docs/SQL_FUNCTIONS.md +1408 -0
- data/docs/TASK_DIAGRAM.md +252 -0
- data/docs/TASK_EXECUTION_CONTROL_FLOW.md +237 -0
- data/docs/TELEMETRY.md +795 -0
- data/docs/TROUBLESHOOTING.md +756 -0
- data/docs/TaskHandlerGenerator.html +255 -0
- data/docs/Tasker/Analysis/RuntimeGraphAnalyzer.html +907 -0
- data/docs/Tasker/Analysis/TemplateGraphAnalyzer.html +1236 -0
- data/docs/Tasker/Analysis.html +117 -0
- data/docs/Tasker/AnalyticsController.html +450 -0
- data/docs/Tasker/AnalyticsService/BottleneckAnalytics.html +816 -0
- data/docs/Tasker/AnalyticsService/PerformanceAnalytics.html +586 -0
- data/docs/Tasker/AnalyticsService.html +2221 -0
- data/docs/Tasker/AnnotationType.html +137 -0
- data/docs/Tasker/AnnotationTypeSerializer.html +124 -0
- data/docs/Tasker/ApplicationController.html +147 -0
- data/docs/Tasker/ApplicationJob.html +128 -0
- data/docs/Tasker/ApplicationRecord.html +378 -0
- data/docs/Tasker/Authentication/AuthenticationError.html +124 -0
- data/docs/Tasker/Authentication/ConfigurationError.html +124 -0
- data/docs/Tasker/Authentication/Coordinator.html +242 -0
- data/docs/Tasker/Authentication/Interface.html +560 -0
- data/docs/Tasker/Authentication/InterfaceError.html +124 -0
- data/docs/Tasker/Authentication/NoneAuthenticator.html +338 -0
- data/docs/Tasker/Authentication.html +119 -0
- data/docs/Tasker/Authorization/AuthorizationError.html +139 -0
- data/docs/Tasker/Authorization/BaseCoordinator.html +927 -0
- data/docs/Tasker/Authorization/ConfigurationError.html +153 -0
- data/docs/Tasker/Authorization/ResourceConstants/ACTIONS.html +428 -0
- data/docs/Tasker/Authorization/ResourceConstants/RESOURCES.html +365 -0
- data/docs/Tasker/Authorization/ResourceConstants.html +146 -0
- data/docs/Tasker/Authorization/ResourceRegistry.html +882 -0
- data/docs/Tasker/Authorization/UnauthorizedError.html +153 -0
- data/docs/Tasker/Authorization.html +582 -0
- data/docs/Tasker/CacheCapabilities.html +167 -0
- data/docs/Tasker/CacheStrategy.html +1297 -0
- data/docs/Tasker/Concerns/Authenticatable.html +116 -0
- data/docs/Tasker/Concerns/Authorizable/AdminStatusChecker.html +256 -0
- data/docs/Tasker/Concerns/Authorizable.html +816 -0
- data/docs/Tasker/Concerns/ControllerAuthorizable.html +157 -0
- data/docs/Tasker/Concerns/EventPublisher.html +4023 -0
- data/docs/Tasker/Concerns/IdempotentStateTransitions.html +806 -0
- data/docs/Tasker/Concerns/LifecycleEventHelpers.html +129 -0
- data/docs/Tasker/Concerns/OrchestrationPublisher.html +129 -0
- data/docs/Tasker/Concerns/StateMachineBase/ClassMethods.html +1075 -0
- data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase/ClassMethods.html +191 -0
- data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase.html +126 -0
- data/docs/Tasker/Concerns/StateMachineBase.html +153 -0
- data/docs/Tasker/Concerns/StructuredLogging.html +1413 -0
- data/docs/Tasker/Concerns.html +117 -0
- data/docs/Tasker/Configuration/AuthConfiguration.html +1023 -0
- data/docs/Tasker/Configuration/ConfigurationProxy.html +581 -0
- data/docs/Tasker/Configuration/DatabaseConfiguration.html +475 -0
- data/docs/Tasker/Configuration/EngineConfiguration.html +1265 -0
- data/docs/Tasker/Configuration/HealthConfiguration.html +791 -0
- data/docs/Tasker/Configuration/TelemetryConfiguration.html +1308 -0
- data/docs/Tasker/Configuration/TelemetryConfigurationProxy.html +388 -0
- data/docs/Tasker/Configuration.html +1669 -0
- data/docs/Tasker/ConfigurationError.html +143 -0
- data/docs/Tasker/ConfiguredTask.html +514 -0
- data/docs/Tasker/Constants/EventDefinitions.html +590 -0
- data/docs/Tasker/Constants/LifecycleEvents.html +137 -0
- data/docs/Tasker/Constants/ObservabilityEvents/Step.html +152 -0
- data/docs/Tasker/Constants/ObservabilityEvents/Task.html +142 -0
- data/docs/Tasker/Constants/ObservabilityEvents.html +126 -0
- data/docs/Tasker/Constants/RegistryEvents.html +285 -0
- data/docs/Tasker/Constants/StepEvents.html +177 -0
- data/docs/Tasker/Constants/TaskEvents.html +167 -0
- data/docs/Tasker/Constants/TaskExecution/ExecutionStatus.html +207 -0
- data/docs/Tasker/Constants/TaskExecution/HealthStatus.html +191 -0
- data/docs/Tasker/Constants/TaskExecution/RecommendedAction.html +207 -0
- data/docs/Tasker/Constants/TaskExecution.html +126 -0
- data/docs/Tasker/Constants/TaskFinalization/ErrorMessages.html +132 -0
- data/docs/Tasker/Constants/TaskFinalization/PendingReasons.html +207 -0
- data/docs/Tasker/Constants/TaskFinalization/ReenqueueReasons.html +239 -0
- data/docs/Tasker/Constants/TaskFinalization.html +126 -0
- data/docs/Tasker/Constants/TaskStatuses.html +223 -0
- data/docs/Tasker/Constants/TestEvents.html +163 -0
- data/docs/Tasker/Constants/WorkflowEvents.html +222 -0
- data/docs/Tasker/Constants/WorkflowStepStatuses.html +223 -0
- data/docs/Tasker/Constants.html +561 -0
- data/docs/Tasker/DependentSystem.html +137 -0
- data/docs/Tasker/DependentSystemObjectMap.html +250 -0
- data/docs/Tasker/DetectorRegistry.html +598 -0
- data/docs/Tasker/Diagram/Edge.html +1191 -0
- data/docs/Tasker/Diagram/Flowchart.html +1539 -0
- data/docs/Tasker/Diagram/Node.html +1165 -0
- data/docs/Tasker/Diagram.html +117 -0
- data/docs/Tasker/Engine.html +215 -0
- data/docs/Tasker/Error.html +139 -0
- data/docs/Tasker/Events/Bus.html +1226 -0
- data/docs/Tasker/Events/Catalog/CatalogPrinter.html +258 -0
- data/docs/Tasker/Events/Catalog/CustomEventRegistrar.html +276 -0
- data/docs/Tasker/Events/Catalog/ExamplePayloadGenerator.html +294 -0
- data/docs/Tasker/Events/Catalog.html +1291 -0
- data/docs/Tasker/Events/CustomRegistry.html +943 -0
- data/docs/Tasker/Events/DefinitionLoader.html +575 -0
- data/docs/Tasker/Events/EventPayloadBuilder/ErrorInfoExtractor.html +286 -0
- data/docs/Tasker/Events/EventPayloadBuilder/StepPayloadBuilder.html +312 -0
- data/docs/Tasker/Events/EventPayloadBuilder.html +664 -0
- data/docs/Tasker/Events/Publisher.html +365 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer/ErrorTypeClassifier.html +1128 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer.html +270 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber/MetricTagsExtractor.html +266 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber.html +2556 -0
- data/docs/Tasker/Events/Subscribers/MetricsSubscriber.html +723 -0
- data/docs/Tasker/Events/Subscribers/TelemetrySubscriber.html +2251 -0
- data/docs/Tasker/Events/Subscribers.html +117 -0
- data/docs/Tasker/Events/SubscriptionLoader.html +493 -0
- data/docs/Tasker/Events.html +294 -0
- data/docs/Tasker/EventsGenerator.html +459 -0
- data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics/AnalyticsMetrics.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics.html +412 -0
- data/docs/Tasker/Functions/FunctionBasedDependencyLevels.html +598 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestSteps/SlowestStep.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestSteps.html +453 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestTasks/SlowestTask.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestTasks.html +453 -0
- data/docs/Tasker/Functions/FunctionBasedStepReadinessStatus.html +1457 -0
- data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts/HealthMetrics.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts.html +370 -0
- data/docs/Tasker/Functions/FunctionBasedTaskExecutionContext.html +1250 -0
- data/docs/Tasker/Functions/FunctionWrapper.html +479 -0
- data/docs/Tasker/Functions.html +117 -0
- data/docs/Tasker/Generators/AuthenticatorGenerator/UsageInstructionsFormatter.html +244 -0
- data/docs/Tasker/Generators/AuthenticatorGenerator.html +373 -0
- data/docs/Tasker/Generators/AuthorizationCoordinatorGenerator.html +430 -0
- data/docs/Tasker/Generators/SubscriberGenerator.html +377 -0
- data/docs/Tasker/Generators/TaskHandlerGenerator.html +263 -0
- data/docs/Tasker/Generators.html +117 -0
- data/docs/Tasker/GraphQLTypes/AnnotationType.html +132 -0
- data/docs/Tasker/GraphQLTypes/BaseArgument.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseConnection.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseEdge.html +130 -0
- data/docs/Tasker/GraphQLTypes/BaseEnum.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseField.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseInputObject.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseInterface.html +116 -0
- data/docs/Tasker/GraphQLTypes/BaseObject.html +128 -0
- data/docs/Tasker/GraphQLTypes/BaseScalar.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseUnion.html +124 -0
- data/docs/Tasker/GraphQLTypes/DependentSystemObjectMapType.html +132 -0
- data/docs/Tasker/GraphQLTypes/DependentSystemType.html +132 -0
- data/docs/Tasker/GraphQLTypes/MutationType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NamedStepType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NamedTaskType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NamedTasksNamedStepType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NodeType.html +118 -0
- data/docs/Tasker/GraphQLTypes/QueryType.html +139 -0
- data/docs/Tasker/GraphQLTypes/TaskAnnotationType.html +132 -0
- data/docs/Tasker/GraphQLTypes/TaskInterface.html +111 -0
- data/docs/Tasker/GraphQLTypes/TaskType.html +201 -0
- data/docs/Tasker/GraphQLTypes/WorkflowStepType.html +694 -0
- data/docs/Tasker/GraphQLTypes.html +130 -0
- data/docs/Tasker/GraphqlController.html +251 -0
- data/docs/Tasker/HandlerFactory.html +1518 -0
- data/docs/Tasker/HandlerSerializer.html +682 -0
- data/docs/Tasker/HandlersController.html +574 -0
- data/docs/Tasker/HashIdentityStrategy.html +278 -0
- data/docs/Tasker/Health/ReadinessChecker.html +712 -0
- data/docs/Tasker/Health/StatusChecker.html +653 -0
- data/docs/Tasker/Health.html +117 -0
- data/docs/Tasker/HealthController.html +523 -0
- data/docs/Tasker/IdentityStrategy.html +276 -0
- data/docs/Tasker/InvalidTaskHandlerConfig.html +135 -0
- data/docs/Tasker/LifecycleEvents/Events/Step.html +162 -0
- data/docs/Tasker/LifecycleEvents/Events/Task.html +162 -0
- data/docs/Tasker/LifecycleEvents/Events.html +204 -0
- data/docs/Tasker/LifecycleEvents/Publisher.html +132 -0
- data/docs/Tasker/LifecycleEvents.html +799 -0
- data/docs/Tasker/Logging/CorrelationIdGenerator.html +688 -0
- data/docs/Tasker/Logging.html +115 -0
- data/docs/Tasker/MetricsController.html +293 -0
- data/docs/Tasker/MetricsExportJob.html +414 -0
- data/docs/Tasker/Mutations/BaseMutation.html +128 -0
- data/docs/Tasker/Mutations/CancelStep.html +219 -0
- data/docs/Tasker/Mutations/CancelTask.html +221 -0
- data/docs/Tasker/Mutations/CreateTask.html +243 -0
- data/docs/Tasker/Mutations/UpdateStep.html +243 -0
- data/docs/Tasker/Mutations/UpdateTask.html +243 -0
- data/docs/Tasker/Mutations.html +117 -0
- data/docs/Tasker/NamedStep.html +216 -0
- data/docs/Tasker/NamedTask.html +910 -0
- data/docs/Tasker/NamedTasksNamedStep.html +435 -0
- data/docs/Tasker/Orchestration/BackoffCalculator.html +404 -0
- data/docs/Tasker/Orchestration/ConnectionBuilder/ConfigValidator.html +258 -0
- data/docs/Tasker/Orchestration/ConnectionBuilder.html +435 -0
- data/docs/Tasker/Orchestration/ConnectionPoolIntelligence.html +513 -0
- data/docs/Tasker/Orchestration/Coordinator.html +641 -0
- data/docs/Tasker/Orchestration/FutureStateAnalyzer.html +1045 -0
- data/docs/Tasker/Orchestration/Orchestrator.html +679 -0
- data/docs/Tasker/Orchestration/PluginIntegration.html +1127 -0
- data/docs/Tasker/Orchestration/ResponseProcessor.html +504 -0
- data/docs/Tasker/Orchestration/RetryHeaderParser.html +304 -0
- data/docs/Tasker/Orchestration/StepExecutor.html +995 -0
- data/docs/Tasker/Orchestration/StepSequenceFactory.html +644 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/BlockageChecker.html +264 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/ContextManager.html +254 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/DelayCalculator.html +556 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationDecisionMaker.html +348 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationProcessor.html +286 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/ReasonDeterminer.html +432 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/ReenqueueManager.html +296 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/UnclearStateHandler.html +314 -0
- data/docs/Tasker/Orchestration/TaskFinalizer.html +1212 -0
- data/docs/Tasker/Orchestration/TaskInitializer.html +766 -0
- data/docs/Tasker/Orchestration/TaskReenqueuer.html +506 -0
- data/docs/Tasker/Orchestration/ViableStepDiscovery.html +442 -0
- data/docs/Tasker/Orchestration/WorkflowCoordinator.html +510 -0
- data/docs/Tasker/Orchestration.html +130 -0
- data/docs/Tasker/PageSort/PageSortParamsBuilder.html +296 -0
- data/docs/Tasker/PageSort.html +247 -0
- data/docs/Tasker/PermanentError.html +518 -0
- data/docs/Tasker/ProceduralError.html +147 -0
- data/docs/Tasker/Queries/AllAnnotationTypes.html +217 -0
- data/docs/Tasker/Queries/AllTasks.html +221 -0
- data/docs/Tasker/Queries/BaseQuery.html +128 -0
- data/docs/Tasker/Queries/Helpers.html +187 -0
- data/docs/Tasker/Queries/OneStep.html +225 -0
- data/docs/Tasker/Queries/OneTask.html +217 -0
- data/docs/Tasker/Queries/TasksByAnnotation.html +231 -0
- data/docs/Tasker/Queries/TasksByStatus.html +233 -0
- data/docs/Tasker/Queries.html +119 -0
- data/docs/Tasker/Railtie.html +124 -0
- data/docs/Tasker/Registry/BaseRegistry.html +1690 -0
- data/docs/Tasker/Registry/EventPublisher.html +667 -0
- data/docs/Tasker/Registry/InterfaceValidator.html +569 -0
- data/docs/Tasker/Registry/RegistrationError.html +132 -0
- data/docs/Tasker/Registry/RegistryError.html +139 -0
- data/docs/Tasker/Registry/StatisticsCollector.html +841 -0
- data/docs/Tasker/Registry/SubscriberRegistry.html +1504 -0
- data/docs/Tasker/Registry/ValidationError.html +132 -0
- data/docs/Tasker/Registry.html +119 -0
- data/docs/Tasker/RetryableError.html +515 -0
- data/docs/Tasker/StateMachine/Compatibility.html +282 -0
- data/docs/Tasker/StateMachine/InvalidStateTransition.html +135 -0
- data/docs/Tasker/StateMachine/StepStateMachine/StandardizedPayloadBuilder.html +260 -0
- data/docs/Tasker/StateMachine/StepStateMachine.html +2215 -0
- data/docs/Tasker/StateMachine/TaskStateMachine.html +734 -0
- data/docs/Tasker/StateMachine.html +602 -0
- data/docs/Tasker/StepDagRelationship.html +657 -0
- data/docs/Tasker/StepHandler/Api/Config.html +1091 -0
- data/docs/Tasker/StepHandler/Api.html +884 -0
- data/docs/Tasker/StepHandler/AutomaticEventPublishing.html +321 -0
- data/docs/Tasker/StepHandler/Base.html +970 -0
- data/docs/Tasker/StepHandler.html +119 -0
- data/docs/Tasker/StepReadinessStatus.html +836 -0
- data/docs/Tasker/Task.html +2575 -0
- data/docs/Tasker/TaskAnnotation.html +137 -0
- data/docs/Tasker/TaskAnnotationSerializer.html +124 -0
- data/docs/Tasker/TaskBuilder/StepNameValidator.html +264 -0
- data/docs/Tasker/TaskBuilder/StepTemplateDefiner.html +264 -0
- data/docs/Tasker/TaskBuilder.html +764 -0
- data/docs/Tasker/TaskDiagram/StepToStepEdgeBuilder.html +260 -0
- data/docs/Tasker/TaskDiagram/TaskToRootStepEdgeBuilder.html +290 -0
- data/docs/Tasker/TaskDiagram.html +548 -0
- data/docs/Tasker/TaskDiagramsController.html +240 -0
- data/docs/Tasker/TaskExecutionContext.html +469 -0
- data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/ClassBasedEventRegistrar.html +238 -0
- data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/YamlEventRegistrar.html +254 -0
- data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner.html +988 -0
- data/docs/Tasker/TaskHandler/ClassMethods.html +357 -0
- data/docs/Tasker/TaskHandler/InstanceMethods.html +1396 -0
- data/docs/Tasker/TaskHandler/StepGroup.html +1748 -0
- data/docs/Tasker/TaskHandler.html +271 -0
- data/docs/Tasker/TaskNamespace.html +312 -0
- data/docs/Tasker/TaskRunnerJob.html +406 -0
- data/docs/Tasker/TaskSerializer.html +474 -0
- data/docs/Tasker/TaskTransition.html +1517 -0
- data/docs/Tasker/TaskWorkflowSummary.html +988 -0
- data/docs/Tasker/TaskerRailsSchema/InvalidObjectTypeError.html +132 -0
- data/docs/Tasker/TaskerRailsSchema/TypeResolutionError.html +139 -0
- data/docs/Tasker/TaskerRailsSchema/UnknownInterfaceError.html +132 -0
- data/docs/Tasker/TaskerRailsSchema.html +384 -0
- data/docs/Tasker/TasksController.html +595 -0
- data/docs/Tasker/Telemetry/EventMapping.html +1307 -0
- data/docs/Tasker/Telemetry/EventRouter.html +2178 -0
- data/docs/Tasker/Telemetry/Events/ExportEvents.html +246 -0
- data/docs/Tasker/Telemetry/Events.html +115 -0
- data/docs/Tasker/Telemetry/ExportCoordinator/DistributedLockTimeoutError.html +135 -0
- data/docs/Tasker/Telemetry/ExportCoordinator.html +2137 -0
- data/docs/Tasker/Telemetry/IntelligentCacheManager.html +1083 -0
- data/docs/Tasker/Telemetry/LogBackend.html +1088 -0
- data/docs/Tasker/Telemetry/MetricTypes/Counter.html +1054 -0
- data/docs/Tasker/Telemetry/MetricTypes/Gauge.html +1270 -0
- data/docs/Tasker/Telemetry/MetricTypes/Histogram.html +1492 -0
- data/docs/Tasker/Telemetry/MetricTypes.html +153 -0
- data/docs/Tasker/Telemetry/MetricsBackend.html +2510 -0
- data/docs/Tasker/Telemetry/MetricsExportService.html +578 -0
- data/docs/Tasker/Telemetry/PluginRegistry.html +1774 -0
- data/docs/Tasker/Telemetry/Plugins/BaseExporter.html +1835 -0
- data/docs/Tasker/Telemetry/Plugins/CsvExporter.html +768 -0
- data/docs/Tasker/Telemetry/Plugins/JsonExporter.html +747 -0
- data/docs/Tasker/Telemetry/Plugins.html +117 -0
- data/docs/Tasker/Telemetry/PrometheusExporter.html +481 -0
- data/docs/Tasker/Telemetry/TraceBackend.html +891 -0
- data/docs/Tasker/Telemetry.html +130 -0
- data/docs/Tasker/Types/AuthConfig.html +886 -0
- data/docs/Tasker/Types/BackoffConfig.html +1063 -0
- data/docs/Tasker/Types/BaseConfig.html +227 -0
- data/docs/Tasker/Types/CacheConfig.html +1731 -0
- data/docs/Tasker/Types/DatabaseConfig.html +388 -0
- data/docs/Tasker/Types/DependencyGraph.html +526 -0
- data/docs/Tasker/Types/DependencyGraphConfig.html +753 -0
- data/docs/Tasker/Types/EngineConfig.html +1181 -0
- data/docs/Tasker/Types/ExecutionConfig.html +1963 -0
- data/docs/Tasker/Types/GraphEdge.html +517 -0
- data/docs/Tasker/Types/GraphMetadata.html +781 -0
- data/docs/Tasker/Types/GraphNode.html +694 -0
- data/docs/Tasker/Types/HealthConfig.html +784 -0
- data/docs/Tasker/Types/StepSequence.html +353 -0
- data/docs/Tasker/Types/StepTemplate.html +1193 -0
- data/docs/Tasker/Types/TaskRequest.html +1179 -0
- data/docs/Tasker/Types/TelemetryConfig.html +2746 -0
- data/docs/Tasker/Types.html +154 -0
- data/docs/Tasker/WorkflowStep/StepFinder.html +282 -0
- data/docs/Tasker/WorkflowStep.html +2724 -0
- data/docs/Tasker/WorkflowStepEdge.html +304 -0
- data/docs/Tasker/WorkflowStepSerializer.html +305 -0
- data/docs/Tasker/WorkflowStepTransition/TransitionDescriptionFormatter.html +282 -0
- data/docs/Tasker/WorkflowStepTransition.html +2201 -0
- data/docs/Tasker/WorkflowStepsController.html +462 -0
- data/docs/Tasker.html +452 -0
- data/docs/VISION.md +584 -0
- data/docs/WHY.md +21 -0
- data/docs/_index.html +2375 -0
- data/docs/class_list.html +54 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +503 -0
- data/docs/events/migration_plan_outcomes.md +80 -0
- data/docs/file.README.html +541 -0
- data/docs/file_list.html +59 -0
- data/docs/frames.html +22 -0
- data/docs/index.html +541 -0
- data/docs/js/app.js +344 -0
- data/docs/js/full_list.js +242 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +9182 -0
- data/docs/top-level-namespace.html +110 -0
- data/lib/generators/tasker/authenticator_generator.rb +301 -0
- data/lib/generators/tasker/authorization_coordinator_generator.rb +139 -0
- data/lib/generators/tasker/events_generator.rb +91 -0
- data/lib/generators/tasker/subscriber_generator.rb +107 -0
- data/lib/generators/tasker/task_handler_generator.rb +138 -0
- data/lib/generators/tasker/templates/api_token_authenticator.rb.erb +113 -0
- data/lib/generators/tasker/templates/api_token_authenticator_spec.rb.erb +144 -0
- data/lib/generators/tasker/templates/authorization_coordinator.rb.erb +95 -0
- data/lib/generators/tasker/templates/authorization_coordinator_spec.rb.erb +142 -0
- data/lib/generators/tasker/templates/custom_authenticator.rb.erb +108 -0
- data/lib/generators/tasker/templates/custom_authenticator_spec.rb.erb +162 -0
- data/lib/generators/tasker/templates/custom_events.yml.erb +62 -0
- data/lib/generators/tasker/templates/custom_subscriber.rb.erb +72 -0
- data/lib/generators/tasker/templates/devise_authenticator.rb.erb +101 -0
- data/lib/generators/tasker/templates/devise_authenticator_spec.rb.erb +126 -0
- data/lib/generators/tasker/templates/initialize.rb.erb +202 -0
- data/lib/generators/tasker/templates/jwt_authenticator.rb.erb +144 -0
- data/lib/generators/tasker/templates/jwt_authenticator_spec.rb.erb +298 -0
- data/lib/generators/tasker/templates/metrics_subscriber.rb.erb +258 -0
- data/lib/generators/tasker/templates/metrics_subscriber_spec.rb.erb +308 -0
- data/lib/generators/tasker/templates/omniauth_authenticator.rb.erb +135 -0
- data/lib/generators/tasker/templates/omniauth_authenticator_spec.rb.erb +196 -0
- data/lib/generators/tasker/templates/opentelemetry_initializer.rb +52 -0
- data/lib/generators/tasker/templates/subscriber.rb.erb +64 -0
- data/lib/generators/tasker/templates/subscriber_spec.rb.erb +80 -0
- data/lib/generators/tasker/templates/task_config.yaml.erb +117 -0
- data/lib/generators/tasker/templates/task_handler.rb.erb +59 -0
- data/lib/generators/tasker/templates/task_handler_spec.rb.erb +159 -0
- data/lib/tasker/analysis/runtime_graph_analyzer.rb +1168 -0
- data/lib/tasker/analysis/template_graph_analyzer.rb +328 -0
- data/lib/tasker/authentication/coordinator.rb +78 -0
- data/lib/tasker/authentication/errors.rb +9 -0
- data/lib/tasker/authentication/interface.rb +36 -0
- data/lib/tasker/authentication/none_authenticator.rb +26 -0
- data/lib/tasker/authorization/base_coordinator.rb +112 -0
- data/lib/tasker/authorization/errors.rb +26 -0
- data/lib/tasker/authorization/resource_constants.rb +74 -0
- data/lib/tasker/authorization/resource_registry.rb +143 -0
- data/lib/tasker/authorization.rb +75 -0
- data/lib/tasker/cache_capabilities.rb +131 -0
- data/lib/tasker/cache_strategy.rb +469 -0
- data/lib/tasker/concerns/authenticatable.rb +41 -0
- data/lib/tasker/concerns/authorizable.rb +204 -0
- data/lib/tasker/concerns/controller_authorizable.rb +124 -0
- data/lib/tasker/concerns/event_publisher.rb +716 -0
- data/lib/tasker/concerns/idempotent_state_transitions.rb +128 -0
- data/lib/tasker/concerns/state_machine_base.rb +218 -0
- data/lib/tasker/concerns/structured_logging.rb +387 -0
- data/lib/tasker/configuration.rb +325 -0
- data/lib/tasker/constants/event_definitions.rb +147 -0
- data/lib/tasker/constants/registry_events.rb +54 -0
- data/lib/tasker/constants.rb +417 -0
- data/lib/tasker/engine.rb +90 -0
- data/lib/tasker/errors.rb +90 -0
- data/lib/tasker/events/catalog.rb +432 -0
- data/lib/tasker/events/custom_registry.rb +175 -0
- data/lib/tasker/events/definition_loader.rb +199 -0
- data/lib/tasker/events/event_payload_builder.rb +461 -0
- data/lib/tasker/events/publisher.rb +149 -0
- data/lib/tasker/events/subscribers/base_subscriber.rb +601 -0
- data/lib/tasker/events/subscribers/metrics_subscriber.rb +120 -0
- data/lib/tasker/events/subscribers/telemetry_subscriber.rb +462 -0
- data/lib/tasker/events/subscription_loader.rb +161 -0
- data/lib/tasker/events.rb +37 -0
- data/lib/tasker/functions/function_based_analytics_metrics.rb +103 -0
- data/lib/tasker/functions/function_based_dependency_levels.rb +54 -0
- data/lib/tasker/functions/function_based_slowest_steps.rb +84 -0
- data/lib/tasker/functions/function_based_slowest_tasks.rb +84 -0
- data/lib/tasker/functions/function_based_step_readiness_status.rb +183 -0
- data/lib/tasker/functions/function_based_system_health_counts.rb +94 -0
- data/lib/tasker/functions/function_based_task_execution_context.rb +148 -0
- data/lib/tasker/functions/function_wrapper.rb +42 -0
- data/lib/tasker/functions.rb +12 -0
- data/lib/tasker/handler_factory.rb +322 -0
- data/lib/tasker/health/readiness_checker.rb +186 -0
- data/lib/tasker/health/status_checker.rb +203 -0
- data/lib/tasker/identity_strategy.rb +38 -0
- data/lib/tasker/logging/correlation_id_generator.rb +120 -0
- data/lib/tasker/orchestration/backoff_calculator.rb +184 -0
- data/lib/tasker/orchestration/connection_builder.rb +122 -0
- data/lib/tasker/orchestration/connection_pool_intelligence.rb +177 -0
- data/lib/tasker/orchestration/coordinator.rb +119 -0
- data/lib/tasker/orchestration/future_state_analyzer.rb +137 -0
- data/lib/tasker/orchestration/plugin_integration.rb +124 -0
- data/lib/tasker/orchestration/response_processor.rb +168 -0
- data/lib/tasker/orchestration/retry_header_parser.rb +78 -0
- data/lib/tasker/orchestration/step_executor.rb +941 -0
- data/lib/tasker/orchestration/step_sequence_factory.rb +67 -0
- data/lib/tasker/orchestration/task_finalizer.rb +564 -0
- data/lib/tasker/orchestration/task_initializer.rb +140 -0
- data/lib/tasker/orchestration/task_reenqueuer.rb +71 -0
- data/lib/tasker/orchestration/viable_step_discovery.rb +65 -0
- data/lib/tasker/orchestration/workflow_coordinator.rb +294 -0
- data/lib/tasker/orchestration.rb +45 -0
- data/lib/tasker/railtie.rb +9 -0
- data/lib/tasker/registry/base_registry.rb +177 -0
- data/lib/tasker/registry/event_publisher.rb +91 -0
- data/lib/tasker/registry/interface_validator.rb +140 -0
- data/lib/tasker/registry/statistics_collector.rb +381 -0
- data/lib/tasker/registry/subscriber_registry.rb +285 -0
- data/lib/tasker/registry.rb +22 -0
- data/lib/tasker/state_machine/step_state_machine.rb +508 -0
- data/lib/tasker/state_machine/task_state_machine.rb +192 -0
- data/lib/tasker/state_machine.rb +83 -0
- data/lib/tasker/step_handler/api.rb +410 -0
- data/lib/tasker/step_handler/base.rb +206 -0
- data/lib/tasker/task_builder.rb +432 -0
- data/lib/tasker/task_handler/class_methods.rb +324 -0
- data/lib/tasker/task_handler/instance_methods.rb +293 -0
- data/lib/tasker/task_handler/step_group.rb +182 -0
- data/lib/tasker/task_handler.rb +43 -0
- data/lib/tasker/telemetry/event_mapping.rb +126 -0
- data/lib/tasker/telemetry/event_router.rb +318 -0
- data/lib/tasker/telemetry/events/export_events.rb +38 -0
- data/lib/tasker/telemetry/export_coordinator.rb +497 -0
- data/lib/tasker/telemetry/intelligent_cache_manager.rb +508 -0
- data/lib/tasker/telemetry/log_backend.rb +224 -0
- data/lib/tasker/telemetry/metric_types.rb +368 -0
- data/lib/tasker/telemetry/metrics_backend.rb +1227 -0
- data/lib/tasker/telemetry/metrics_export_service.rb +392 -0
- data/lib/tasker/telemetry/plugin_registry.rb +333 -0
- data/lib/tasker/telemetry/plugins/base_exporter.rb +246 -0
- data/lib/tasker/telemetry/plugins/csv_exporter.rb +198 -0
- data/lib/tasker/telemetry/plugins/json_exporter.rb +141 -0
- data/lib/tasker/telemetry/prometheus_exporter.rb +249 -0
- data/lib/tasker/telemetry/trace_backend.rb +186 -0
- data/lib/tasker/telemetry.rb +59 -0
- data/lib/tasker/types/auth_config.rb +81 -0
- data/lib/tasker/types/backoff_config.rb +142 -0
- data/lib/tasker/types/cache_config.rb +257 -0
- data/lib/tasker/types/database_config.rb +39 -0
- data/lib/tasker/types/dependency_graph.rb +225 -0
- data/lib/tasker/types/dependency_graph_config.rb +149 -0
- data/lib/tasker/types/engine_config.rb +131 -0
- data/lib/tasker/types/execution_config.rb +289 -0
- data/lib/tasker/types/health_config.rb +84 -0
- data/lib/tasker/types/step_sequence.rb +24 -0
- data/lib/tasker/types/step_template.rb +63 -0
- data/lib/tasker/types/task_request.rb +60 -0
- data/lib/tasker/types/telemetry_config.rb +273 -0
- data/lib/tasker/types.rb +64 -0
- data/lib/tasker/version.rb +7 -0
- data/lib/tasker.rb +82 -0
- data/lib/tasks/tasker_tasks.rake +302 -0
- metadata +958 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent-ruby'
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module Tasker
|
7
|
+
module Telemetry
|
8
|
+
# LogBackend provides thread-safe structured logging for events
|
9
|
+
#
|
10
|
+
# This class implements Tasker's core logging system with thread-safe operations,
|
11
|
+
# automatic EventRouter integration, and structured log data collection.
|
12
|
+
# It complements Rails logging with structured event data suitable for
|
13
|
+
# log aggregation systems like ELK, Splunk, or Fluentd.
|
14
|
+
#
|
15
|
+
# The backend follows the same singleton pattern as MetricsBackend for consistency
|
16
|
+
# and provides structured log data with correlation IDs and contextual information.
|
17
|
+
#
|
18
|
+
# @example Basic usage
|
19
|
+
# backend = LogBackend.instance
|
20
|
+
# backend.log_event('task.started', { task_id: '123', level: 'info' })
|
21
|
+
#
|
22
|
+
# @example EventRouter integration
|
23
|
+
# # Automatic log collection based on event routing
|
24
|
+
# backend.handle_event('task.failed', { task_id: '123', error: 'timeout', level: 'error' })
|
25
|
+
#
|
26
|
+
class LogBackend
|
27
|
+
include Singleton
|
28
|
+
|
29
|
+
# Core log storage for structured events
|
30
|
+
# Using ConcurrentHash for thread-safe operations without locks
|
31
|
+
# @return [Concurrent::Hash] Thread-safe log storage
|
32
|
+
attr_reader :logs
|
33
|
+
|
34
|
+
# Backend creation timestamp for monitoring
|
35
|
+
# @return [Time] When this backend was initialized
|
36
|
+
attr_reader :created_at
|
37
|
+
|
38
|
+
# Log levels in order of severity
|
39
|
+
LOG_LEVELS = %w[debug info warn error fatal].freeze
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
@logs = Concurrent::Hash.new { |h, k| h[k] = Concurrent::Array.new }
|
43
|
+
@created_at = Time.current
|
44
|
+
@instance_id = Socket.gethostname
|
45
|
+
end
|
46
|
+
|
47
|
+
# Handle an event from EventRouter and collect appropriate log data
|
48
|
+
#
|
49
|
+
# This method is called by EventRouter when an event should be routed to
|
50
|
+
# the log backend. It automatically creates structured log entries based on
|
51
|
+
# event type and payload.
|
52
|
+
#
|
53
|
+
# @param event_name [String] The lifecycle event name
|
54
|
+
# @param payload [Hash] Event payload with log data
|
55
|
+
# @return [Boolean] True if log data was collected successfully
|
56
|
+
#
|
57
|
+
# @example Automatic usage via EventRouter
|
58
|
+
# # EventRouter calls this automatically:
|
59
|
+
# backend.handle_event('task.failed', {
|
60
|
+
# task_id: '123',
|
61
|
+
# error: 'Payment gateway timeout',
|
62
|
+
# context: { user_id: 456, amount: 100.0 }
|
63
|
+
# })
|
64
|
+
#
|
65
|
+
def handle_event(event_name, payload = {})
|
66
|
+
return false unless payload.is_a?(Hash)
|
67
|
+
|
68
|
+
log_entry = {
|
69
|
+
timestamp: Time.current.iso8601,
|
70
|
+
event_name: event_name,
|
71
|
+
level: determine_log_level(event_name),
|
72
|
+
message: build_log_message(event_name, payload),
|
73
|
+
payload: payload,
|
74
|
+
instance_id: @instance_id,
|
75
|
+
correlation_id: extract_correlation_id(payload)
|
76
|
+
}
|
77
|
+
|
78
|
+
# Store log entry by level for organized retrieval
|
79
|
+
level = log_entry[:level]
|
80
|
+
@logs[level] << log_entry
|
81
|
+
|
82
|
+
# Also store in chronological order
|
83
|
+
@logs[:all] << log_entry
|
84
|
+
|
85
|
+
true
|
86
|
+
rescue StandardError => e
|
87
|
+
# Log error but don't raise to prevent breaking the event flow
|
88
|
+
Rails.logger&.error("LogBackend error handling #{event_name}: #{e.message}")
|
89
|
+
false
|
90
|
+
end
|
91
|
+
|
92
|
+
# Export all collected log data
|
93
|
+
#
|
94
|
+
# @param level [String, nil] Specific log level to export, or nil for all
|
95
|
+
# @return [Hash] Log data with metadata
|
96
|
+
def export(level: nil)
|
97
|
+
logs_to_export = if level && LOG_LEVELS.include?(level.to_s)
|
98
|
+
{ level.to_s => @logs[level.to_s].to_a }
|
99
|
+
else
|
100
|
+
@logs.each_with_object({}) { |(k, v), h| h[k.to_s] = v.to_a }
|
101
|
+
end
|
102
|
+
|
103
|
+
{
|
104
|
+
logs: logs_to_export,
|
105
|
+
metadata: {
|
106
|
+
backend: 'log',
|
107
|
+
instance_id: @instance_id,
|
108
|
+
created_at: @created_at.iso8601,
|
109
|
+
exported_at: Time.current.iso8601,
|
110
|
+
total_entries: @logs[:all]&.size || 0,
|
111
|
+
level_counts: LOG_LEVELS.index_with { |l| @logs[l]&.size || 0 }
|
112
|
+
}
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
# Clear all log data (primarily for testing)
|
117
|
+
#
|
118
|
+
# @return [void]
|
119
|
+
def reset!
|
120
|
+
@logs.clear
|
121
|
+
end
|
122
|
+
|
123
|
+
# Get log statistics
|
124
|
+
#
|
125
|
+
# @return [Hash] Statistics about collected logs
|
126
|
+
def stats
|
127
|
+
{
|
128
|
+
total_entries: @logs[:all]&.size || 0,
|
129
|
+
level_counts: LOG_LEVELS.index_with { |level| @logs[level]&.size || 0 },
|
130
|
+
backend_uptime: Time.current - @created_at,
|
131
|
+
instance_id: @instance_id
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
# Get recent log entries
|
136
|
+
#
|
137
|
+
# @param limit [Integer] Number of recent entries to return
|
138
|
+
# @param level [String, nil] Specific log level to filter by
|
139
|
+
# @return [Array<Hash>] Recent log entries
|
140
|
+
def recent_entries(limit: 100, level: nil)
|
141
|
+
entries = if level && LOG_LEVELS.include?(level.to_s)
|
142
|
+
@logs[level.to_s].to_a
|
143
|
+
else
|
144
|
+
@logs[:all].to_a
|
145
|
+
end
|
146
|
+
|
147
|
+
entries.last(limit)
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def determine_log_level(event_name)
|
153
|
+
case event_name
|
154
|
+
when /\.failed$/, /\.error$/
|
155
|
+
'error'
|
156
|
+
when /\.warn$/, /\.warning$/
|
157
|
+
'warn'
|
158
|
+
when /\.started$/, /\.completed$/
|
159
|
+
'info'
|
160
|
+
when /\.debug$/, /\.trace$/
|
161
|
+
'debug'
|
162
|
+
else
|
163
|
+
'info'
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def build_log_message(event_name, payload)
|
168
|
+
# Create a human-readable message from the event and payload
|
169
|
+
entity_type = extract_entity_type(event_name)
|
170
|
+
action = extract_action(event_name)
|
171
|
+
|
172
|
+
base_message = "#{entity_type.capitalize} #{action}"
|
173
|
+
|
174
|
+
# Add key details from payload
|
175
|
+
details = []
|
176
|
+
details << "ID: #{payload[:task_id]}" if payload[:task_id]
|
177
|
+
details << "Step: #{payload[:workflow_step_id]}" if payload[:workflow_step_id]
|
178
|
+
details << "Error: #{payload[:error]}" if payload[:error]
|
179
|
+
details << "Duration: #{payload[:duration]}s" if payload[:duration]
|
180
|
+
|
181
|
+
details.empty? ? base_message : "#{base_message} (#{details.join(', ')})"
|
182
|
+
end
|
183
|
+
|
184
|
+
def extract_entity_type(event_name)
|
185
|
+
case event_name
|
186
|
+
when /^task\./
|
187
|
+
'task'
|
188
|
+
when /^step\./
|
189
|
+
'step'
|
190
|
+
when /^workflow\./
|
191
|
+
'workflow'
|
192
|
+
else
|
193
|
+
'system'
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def extract_action(event_name)
|
198
|
+
case event_name
|
199
|
+
when /\.started$/
|
200
|
+
'started'
|
201
|
+
when /\.completed$/
|
202
|
+
'completed'
|
203
|
+
when /\.failed$/
|
204
|
+
'failed'
|
205
|
+
when /\.before_/
|
206
|
+
'preparing'
|
207
|
+
when /\.after_/
|
208
|
+
'finished'
|
209
|
+
else
|
210
|
+
'event'
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def extract_correlation_id(payload)
|
215
|
+
# Extract correlation ID for log tracing
|
216
|
+
payload[:correlation_id] ||
|
217
|
+
payload[:request_id] ||
|
218
|
+
payload[:task_id] ||
|
219
|
+
payload[:workflow_step_id] ||
|
220
|
+
SecureRandom.uuid
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,368 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent-ruby'
|
4
|
+
|
5
|
+
module Tasker
|
6
|
+
module Telemetry
|
7
|
+
# MetricTypes provides thread-safe metric storage classes for high-performance telemetry
|
8
|
+
#
|
9
|
+
# This module implements the core metric types for Tasker's native metrics collection
|
10
|
+
# backend. All metric types are thread-safe using atomic operations and follow
|
11
|
+
# Tasker's fail-fast principles with explicit error handling.
|
12
|
+
#
|
13
|
+
# @example Basic metric operations
|
14
|
+
# counter = Counter.new('tasks_completed')
|
15
|
+
# counter.increment # +1
|
16
|
+
# counter.increment(5) # +5
|
17
|
+
# counter.value # → current count
|
18
|
+
#
|
19
|
+
# @example Gauge operations
|
20
|
+
# gauge = Gauge.new('active_tasks')
|
21
|
+
# gauge.set(42) # Set to specific value
|
22
|
+
# gauge.increment # +1
|
23
|
+
# gauge.decrement(3) # -3
|
24
|
+
#
|
25
|
+
module MetricTypes
|
26
|
+
# Counter represents a monotonically increasing metric value
|
27
|
+
#
|
28
|
+
# Counters are thread-safe and use atomic operations for increment operations.
|
29
|
+
# They follow the fail-fast principle with explicit validation and meaningful
|
30
|
+
# error messages for invalid operations.
|
31
|
+
#
|
32
|
+
# @example Production usage
|
33
|
+
# counter = Counter.new('api_requests_total', labels: { endpoint: '/tasks' })
|
34
|
+
# counter.increment # Increment by 1
|
35
|
+
# counter.increment(batch_size) # Increment by batch size
|
36
|
+
# counter.value # Get current value (thread-safe read)
|
37
|
+
#
|
38
|
+
class Counter
|
39
|
+
# @return [String] The metric name
|
40
|
+
attr_reader :name
|
41
|
+
|
42
|
+
# @return [Hash] The metric labels for dimensional data
|
43
|
+
attr_reader :labels
|
44
|
+
|
45
|
+
# @return [Time] When this metric was first created
|
46
|
+
attr_reader :created_at
|
47
|
+
|
48
|
+
# Initialize a new counter metric
|
49
|
+
#
|
50
|
+
# @param name [String] The metric name (must be present)
|
51
|
+
# @param labels [Hash] Optional labels for dimensional metrics
|
52
|
+
# @raise [ArgumentError] If name is nil or empty
|
53
|
+
def initialize(name, labels: {})
|
54
|
+
raise ArgumentError, 'Metric name cannot be nil or empty' if name.nil? || name.strip.empty?
|
55
|
+
|
56
|
+
@name = name.to_s.freeze
|
57
|
+
@labels = labels.freeze
|
58
|
+
@value = Concurrent::AtomicFixnum.new(0)
|
59
|
+
@created_at = Time.current.freeze
|
60
|
+
end
|
61
|
+
|
62
|
+
# Increment the counter by a specified amount
|
63
|
+
#
|
64
|
+
# @param amount [Integer] Amount to increment (must be non-negative)
|
65
|
+
# @return [Integer] The new counter value
|
66
|
+
# @raise [ArgumentError] If amount is negative or not an integer
|
67
|
+
def increment(amount = 1)
|
68
|
+
return false unless amount.is_a?(Integer)
|
69
|
+
raise ArgumentError, "Counter increment amount must be positive, got: #{amount}" if amount.negative?
|
70
|
+
|
71
|
+
@value.update { |current| current + amount }
|
72
|
+
end
|
73
|
+
|
74
|
+
# Get the current counter value (thread-safe read)
|
75
|
+
#
|
76
|
+
# @return [Integer] Current counter value
|
77
|
+
delegate :value, to: :@value
|
78
|
+
|
79
|
+
# Reset the counter to zero (primarily for testing)
|
80
|
+
#
|
81
|
+
# @return [Integer] The reset value (0)
|
82
|
+
def reset!
|
83
|
+
@value.value = 0
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get a hash representation of this metric
|
87
|
+
#
|
88
|
+
# @return [Hash] Metric data including name, labels, value, type
|
89
|
+
def to_h
|
90
|
+
{
|
91
|
+
name: name,
|
92
|
+
labels: labels,
|
93
|
+
value: value,
|
94
|
+
type: :counter,
|
95
|
+
created_at: created_at
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
# Get a description of this metric for debugging
|
100
|
+
#
|
101
|
+
# @return [String] Human-readable description
|
102
|
+
def description
|
103
|
+
label_str = labels.empty? ? '' : labels.inspect
|
104
|
+
"#{name}#{label_str} = #{value} (counter)"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Gauge represents a metric value that can go up or down
|
109
|
+
#
|
110
|
+
# Gauges are thread-safe and support atomic set, increment, and decrement
|
111
|
+
# operations. They follow fail-fast principles with explicit validation.
|
112
|
+
#
|
113
|
+
# @example Production usage
|
114
|
+
# gauge = Gauge.new('active_connections')
|
115
|
+
# gauge.set(100) # Set to specific value
|
116
|
+
# gauge.increment(5) # +5 connections
|
117
|
+
# gauge.decrement(2) # -2 connections
|
118
|
+
# gauge.value # Get current value
|
119
|
+
#
|
120
|
+
class Gauge
|
121
|
+
# @return [String] The metric name
|
122
|
+
attr_reader :name
|
123
|
+
|
124
|
+
# @return [Hash] The metric labels for dimensional data
|
125
|
+
attr_reader :labels
|
126
|
+
|
127
|
+
# @return [Time] When this metric was first created
|
128
|
+
attr_reader :created_at
|
129
|
+
|
130
|
+
# Initialize a new gauge metric
|
131
|
+
#
|
132
|
+
# @param name [String] The metric name (must be present)
|
133
|
+
# @param labels [Hash] Optional labels for dimensional metrics
|
134
|
+
# @param initial_value [Numeric] Initial gauge value
|
135
|
+
# @raise [ArgumentError] If name is nil or empty, or initial_value is not numeric
|
136
|
+
def initialize(name, labels: {}, initial_value: 0)
|
137
|
+
raise ArgumentError, 'Metric name cannot be nil or empty' if name.nil? || name.strip.empty?
|
138
|
+
|
139
|
+
unless initial_value.is_a?(Numeric)
|
140
|
+
raise ArgumentError,
|
141
|
+
"Initial value must be numeric, got: #{initial_value.class}"
|
142
|
+
end
|
143
|
+
|
144
|
+
@name = name.to_s.freeze
|
145
|
+
@labels = labels.freeze
|
146
|
+
@value = Concurrent::AtomicReference.new(initial_value)
|
147
|
+
@created_at = Time.current.freeze
|
148
|
+
end
|
149
|
+
|
150
|
+
# Set the gauge to a specific value
|
151
|
+
#
|
152
|
+
# @param new_value [Numeric] The new gauge value
|
153
|
+
# @return [Numeric] The new gauge value
|
154
|
+
# @raise [ArgumentError] If new_value is not numeric
|
155
|
+
def set(new_value)
|
156
|
+
raise ArgumentError, "Gauge value must be numeric, got: #{new_value.class}" unless new_value.is_a?(Numeric)
|
157
|
+
|
158
|
+
@value.set(new_value)
|
159
|
+
new_value
|
160
|
+
end
|
161
|
+
|
162
|
+
# Increment the gauge by a specified amount
|
163
|
+
#
|
164
|
+
# @param amount [Numeric] Amount to increment (can be negative)
|
165
|
+
# @return [Numeric] The new gauge value
|
166
|
+
# @raise [ArgumentError] If amount is not numeric
|
167
|
+
def increment(amount = 1)
|
168
|
+
unless amount.is_a?(Numeric)
|
169
|
+
raise ArgumentError,
|
170
|
+
"Gauge increment amount must be numeric, got: #{amount.class}"
|
171
|
+
end
|
172
|
+
|
173
|
+
@value.update { |current| current + amount }
|
174
|
+
end
|
175
|
+
|
176
|
+
# Decrement the gauge by a specified amount
|
177
|
+
#
|
178
|
+
# @param amount [Numeric] Amount to decrement (must be positive)
|
179
|
+
# @return [Numeric] The new gauge value
|
180
|
+
# @raise [ArgumentError] If amount is not numeric or is negative
|
181
|
+
def decrement(amount = 1)
|
182
|
+
unless amount.is_a?(Numeric)
|
183
|
+
raise ArgumentError,
|
184
|
+
"Gauge decrement amount must be numeric, got: #{amount.class}"
|
185
|
+
end
|
186
|
+
raise ArgumentError, "Gauge decrement amount must be positive, got: #{amount}" if amount.negative?
|
187
|
+
|
188
|
+
increment(-amount)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Get the current gauge value (thread-safe read)
|
192
|
+
#
|
193
|
+
# @return [Numeric] Current gauge value
|
194
|
+
def value
|
195
|
+
@value.get
|
196
|
+
end
|
197
|
+
|
198
|
+
# Get a hash representation of this metric
|
199
|
+
#
|
200
|
+
# @return [Hash] Metric data including name, labels, value, type
|
201
|
+
def to_h
|
202
|
+
{
|
203
|
+
name: name,
|
204
|
+
labels: labels,
|
205
|
+
value: value,
|
206
|
+
type: :gauge,
|
207
|
+
created_at: created_at
|
208
|
+
}
|
209
|
+
end
|
210
|
+
|
211
|
+
# Get a description of this metric for debugging
|
212
|
+
#
|
213
|
+
# @return [String] Human-readable description
|
214
|
+
def description
|
215
|
+
label_str = labels.empty? ? '' : labels.inspect
|
216
|
+
"#{name}#{label_str} = #{value} (gauge)"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Histogram represents a metric that samples observations and counts them in buckets
|
221
|
+
#
|
222
|
+
# Histograms are thread-safe and provide statistical analysis of observed values.
|
223
|
+
# They track count, sum, and bucket distributions for duration and size metrics.
|
224
|
+
#
|
225
|
+
# @example Production usage
|
226
|
+
# histogram = Histogram.new('task_duration_seconds', buckets: [0.1, 0.5, 1.0, 5.0])
|
227
|
+
# histogram.observe(0.45) # Record a 0.45 second duration
|
228
|
+
# histogram.observe(2.1) # Record a 2.1 second duration
|
229
|
+
# histogram.count # Total observations
|
230
|
+
# histogram.sum # Sum of all observed values
|
231
|
+
# histogram.buckets # Bucket counts
|
232
|
+
#
|
233
|
+
class Histogram
|
234
|
+
# @return [String] The metric name
|
235
|
+
attr_reader :name
|
236
|
+
|
237
|
+
# @return [Hash] The metric labels for dimensional data
|
238
|
+
attr_reader :labels
|
239
|
+
|
240
|
+
# @return [Array<Numeric>] The histogram bucket boundaries
|
241
|
+
attr_reader :bucket_boundaries
|
242
|
+
|
243
|
+
# @return [Time] When this metric was first created
|
244
|
+
attr_reader :created_at
|
245
|
+
|
246
|
+
# Default bucket boundaries for duration metrics (in seconds)
|
247
|
+
DEFAULT_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0].freeze
|
248
|
+
|
249
|
+
# Initialize a new histogram metric
|
250
|
+
#
|
251
|
+
# @param name [String] The metric name (must be present)
|
252
|
+
# @param labels [Hash] Optional labels for dimensional metrics
|
253
|
+
# @param buckets [Array<Numeric>] Bucket boundaries (must be sorted ascending)
|
254
|
+
# @raise [ArgumentError] If name is nil/empty or buckets are invalid
|
255
|
+
def initialize(name, labels: {}, buckets: DEFAULT_BUCKETS)
|
256
|
+
raise ArgumentError, 'Metric name cannot be nil or empty' if name.nil? || name.strip.empty?
|
257
|
+
raise ArgumentError, 'Buckets must be an array' unless buckets.is_a?(Array)
|
258
|
+
raise ArgumentError, 'Buckets cannot be empty' if buckets.empty?
|
259
|
+
|
260
|
+
@name = name.to_s.freeze
|
261
|
+
@labels = labels.freeze
|
262
|
+
@bucket_boundaries = buckets.sort.freeze
|
263
|
+
@created_at = Time.current.freeze
|
264
|
+
|
265
|
+
# Thread-safe counters for each bucket + infinity bucket
|
266
|
+
@bucket_counts = (@bucket_boundaries + [Float::INFINITY]).map do |_|
|
267
|
+
Concurrent::AtomicFixnum.new(0)
|
268
|
+
end
|
269
|
+
|
270
|
+
@count = Concurrent::AtomicFixnum.new(0)
|
271
|
+
@sum = Concurrent::AtomicReference.new(0.0)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Observe a value and update histogram buckets
|
275
|
+
#
|
276
|
+
# @param value [Numeric] The observed value
|
277
|
+
# @return [Numeric] The observed value (for chaining)
|
278
|
+
# @raise [ArgumentError] If value is not numeric
|
279
|
+
def observe(value)
|
280
|
+
raise ArgumentError, "Observed value must be numeric, got: #{value.class}" unless value.is_a?(Numeric)
|
281
|
+
|
282
|
+
# Update count and sum atomically
|
283
|
+
@count.increment
|
284
|
+
@sum.update { |current| current + value }
|
285
|
+
|
286
|
+
# Increment all buckets where value <= boundary (cumulative histogram)
|
287
|
+
@bucket_boundaries.each_with_index do |boundary, index|
|
288
|
+
@bucket_counts[index].increment if value <= boundary
|
289
|
+
end
|
290
|
+
|
291
|
+
# Always increment the infinity bucket (total count)
|
292
|
+
@bucket_counts.last.increment
|
293
|
+
|
294
|
+
value
|
295
|
+
end
|
296
|
+
|
297
|
+
# Get the total number of observations
|
298
|
+
#
|
299
|
+
# @return [Integer] Total observation count
|
300
|
+
def count
|
301
|
+
@count.value
|
302
|
+
end
|
303
|
+
|
304
|
+
# Get the sum of all observed values
|
305
|
+
#
|
306
|
+
# @return [Numeric] Sum of observations
|
307
|
+
def sum
|
308
|
+
@sum.get
|
309
|
+
end
|
310
|
+
|
311
|
+
# Get the current bucket counts
|
312
|
+
#
|
313
|
+
# @return [Hash] Bucket boundaries to counts mapping
|
314
|
+
def buckets
|
315
|
+
result = {}
|
316
|
+
@bucket_boundaries.each_with_index do |boundary, index|
|
317
|
+
result[boundary] = @bucket_counts[index].value
|
318
|
+
end
|
319
|
+
result[Float::INFINITY] = @bucket_counts.last.value
|
320
|
+
result
|
321
|
+
end
|
322
|
+
|
323
|
+
# Calculate the average of observed values
|
324
|
+
#
|
325
|
+
# @return [Float] Average value, or 0.0 if no observations
|
326
|
+
def average
|
327
|
+
current_count = count
|
328
|
+
return 0.0 if current_count.zero?
|
329
|
+
|
330
|
+
sum.to_f / current_count
|
331
|
+
end
|
332
|
+
|
333
|
+
# Get a hash representation of this metric
|
334
|
+
#
|
335
|
+
# @return [Hash] Metric data including name, labels, buckets, count, sum, type
|
336
|
+
def to_h
|
337
|
+
{
|
338
|
+
name: name,
|
339
|
+
labels: labels,
|
340
|
+
buckets: buckets,
|
341
|
+
count: count,
|
342
|
+
sum: sum,
|
343
|
+
average: average,
|
344
|
+
type: :histogram,
|
345
|
+
created_at: created_at
|
346
|
+
}
|
347
|
+
end
|
348
|
+
|
349
|
+
# Get a description of this metric for debugging
|
350
|
+
#
|
351
|
+
# @return [String] Human-readable description
|
352
|
+
def description
|
353
|
+
label_str = labels.empty? ? '' : labels.inspect
|
354
|
+
"#{name}#{label_str} = #{count} observations, avg: #{average.round(3)} (histogram)"
|
355
|
+
end
|
356
|
+
|
357
|
+
# Reset the histogram (primarily for testing)
|
358
|
+
#
|
359
|
+
# @return [void]
|
360
|
+
def reset!
|
361
|
+
@count.value = 0
|
362
|
+
@sum.set(0.0)
|
363
|
+
@bucket_counts.each { |bucket| bucket.value = 0 }
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|