tasker-engine 0.1.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.
Files changed (601) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +440 -0
  4. data/Rakefile +10 -0
  5. data/app/controllers/tasker/analytics_controller.rb +179 -0
  6. data/app/controllers/tasker/application_controller.rb +45 -0
  7. data/app/controllers/tasker/graphql_controller.rb +193 -0
  8. data/app/controllers/tasker/handlers_controller.rb +217 -0
  9. data/app/controllers/tasker/health_controller.rb +229 -0
  10. data/app/controllers/tasker/metrics_controller.rb +111 -0
  11. data/app/controllers/tasker/page_sort.rb +97 -0
  12. data/app/controllers/tasker/tasks_controller.rb +123 -0
  13. data/app/controllers/tasker/workflow_steps_controller.rb +69 -0
  14. data/app/graphql/examples/all_tasks.graphql +22 -0
  15. data/app/graphql/examples/pending_tasks.graphql +23 -0
  16. data/app/graphql/tasker/graph_ql_types/annotation_type.rb +14 -0
  17. data/app/graphql/tasker/graph_ql_types/base_argument.rb +9 -0
  18. data/app/graphql/tasker/graph_ql_types/base_connection.rb +11 -0
  19. data/app/graphql/tasker/graph_ql_types/base_edge.rb +10 -0
  20. data/app/graphql/tasker/graph_ql_types/base_enum.rb +9 -0
  21. data/app/graphql/tasker/graph_ql_types/base_field.rb +10 -0
  22. data/app/graphql/tasker/graph_ql_types/base_input_object.rb +10 -0
  23. data/app/graphql/tasker/graph_ql_types/base_interface.rb +14 -0
  24. data/app/graphql/tasker/graph_ql_types/base_object.rb +10 -0
  25. data/app/graphql/tasker/graph_ql_types/base_scalar.rb +9 -0
  26. data/app/graphql/tasker/graph_ql_types/base_union.rb +11 -0
  27. data/app/graphql/tasker/graph_ql_types/dependent_system_object_map_type.rb +18 -0
  28. data/app/graphql/tasker/graph_ql_types/dependent_system_type.rb +13 -0
  29. data/app/graphql/tasker/graph_ql_types/mutation_type.rb +16 -0
  30. data/app/graphql/tasker/graph_ql_types/named_step_type.rb +16 -0
  31. data/app/graphql/tasker/graph_ql_types/named_task_type.rb +14 -0
  32. data/app/graphql/tasker/graph_ql_types/named_tasks_named_step_type.rb +19 -0
  33. data/app/graphql/tasker/graph_ql_types/node_type.rb +12 -0
  34. data/app/graphql/tasker/graph_ql_types/query_type.rb +20 -0
  35. data/app/graphql/tasker/graph_ql_types/task_annotation_type.rb +17 -0
  36. data/app/graphql/tasker/graph_ql_types/task_interface.rb +17 -0
  37. data/app/graphql/tasker/graph_ql_types/task_type.rb +26 -0
  38. data/app/graphql/tasker/graph_ql_types/workflow_step_type.rb +154 -0
  39. data/app/graphql/tasker/graph_ql_types.rb +42 -0
  40. data/app/graphql/tasker/mutations/base_mutation.rb +13 -0
  41. data/app/graphql/tasker/mutations/cancel_step.rb +29 -0
  42. data/app/graphql/tasker/mutations/cancel_task.rb +29 -0
  43. data/app/graphql/tasker/mutations/create_task.rb +52 -0
  44. data/app/graphql/tasker/mutations/update_step.rb +36 -0
  45. data/app/graphql/tasker/mutations/update_task.rb +41 -0
  46. data/app/graphql/tasker/queries/all_annotation_types.rb +17 -0
  47. data/app/graphql/tasker/queries/all_tasks.rb +23 -0
  48. data/app/graphql/tasker/queries/base_query.rb +9 -0
  49. data/app/graphql/tasker/queries/helpers.rb +16 -0
  50. data/app/graphql/tasker/queries/one_step.rb +24 -0
  51. data/app/graphql/tasker/queries/one_task.rb +18 -0
  52. data/app/graphql/tasker/queries/tasks_by_annotation.rb +31 -0
  53. data/app/graphql/tasker/queries/tasks_by_status.rb +30 -0
  54. data/app/graphql/tasker/tasker_rails_schema.rb +52 -0
  55. data/app/jobs/tasker/application_job.rb +8 -0
  56. data/app/jobs/tasker/metrics_export_job.rb +252 -0
  57. data/app/jobs/tasker/task_runner_job.rb +224 -0
  58. data/app/models/tasker/annotation_type.rb +26 -0
  59. data/app/models/tasker/application_record.rb +70 -0
  60. data/app/models/tasker/dependent_system.rb +26 -0
  61. data/app/models/tasker/dependent_system_object_map.rb +64 -0
  62. data/app/models/tasker/named_step.rb +41 -0
  63. data/app/models/tasker/named_task.rb +121 -0
  64. data/app/models/tasker/named_tasks_named_step.rb +82 -0
  65. data/app/models/tasker/step_dag_relationship.rb +65 -0
  66. data/app/models/tasker/step_readiness_status.rb +59 -0
  67. data/app/models/tasker/task.rb +414 -0
  68. data/app/models/tasker/task_annotation.rb +36 -0
  69. data/app/models/tasker/task_execution_context.rb +29 -0
  70. data/app/models/tasker/task_namespace.rb +41 -0
  71. data/app/models/tasker/task_transition.rb +235 -0
  72. data/app/models/tasker/workflow_step.rb +461 -0
  73. data/app/models/tasker/workflow_step_edge.rb +95 -0
  74. data/app/models/tasker/workflow_step_transition.rb +434 -0
  75. data/app/serializers/tasker/annotation_type_serializer.rb +8 -0
  76. data/app/serializers/tasker/handler_serializer.rb +109 -0
  77. data/app/serializers/tasker/task_annotation_serializer.rb +32 -0
  78. data/app/serializers/tasker/task_serializer.rb +168 -0
  79. data/app/serializers/tasker/workflow_step_serializer.rb +27 -0
  80. data/app/services/tasker/analytics_service.rb +409 -0
  81. data/config/initializers/dry_struct.rb +11 -0
  82. data/config/initializers/statesman.rb +6 -0
  83. data/config/initializers/tasker_orchestration.rb +17 -0
  84. data/config/initializers/time_formats.rb +4 -0
  85. data/config/routes.rb +34 -0
  86. data/config/tasker/subscriptions/example_integrations.yml +67 -0
  87. data/config/tasker/system_events.yml +305 -0
  88. data/db/functions/calculate_dependency_levels_v01.sql +45 -0
  89. data/db/functions/get_analytics_metrics_v01.sql +137 -0
  90. data/db/functions/get_slowest_steps_v01.sql +82 -0
  91. data/db/functions/get_slowest_tasks_v01.sql +96 -0
  92. data/db/functions/get_step_readiness_status_batch_v01.sql +140 -0
  93. data/db/functions/get_step_readiness_status_single_and_batch_v02.sql +223 -0
  94. data/db/functions/get_step_readiness_status_v01.sql +139 -0
  95. data/db/functions/get_system_health_counts_v01.sql +108 -0
  96. data/db/functions/get_task_execution_context_v01.sql +108 -0
  97. data/db/functions/get_task_execution_contexts_batch_v01.sql +104 -0
  98. data/db/init/schema.sql +2254 -0
  99. data/db/migrate/20250701165431_initial_tasker_schema.rb +116 -0
  100. data/db/migrate/20250710110830_step_readiness_sql_functions_v02.rb +39 -0
  101. data/db/views/tasker_step_dag_relationships_v01.sql +69 -0
  102. data/docs/APPLICATION_GENERATOR.md +384 -0
  103. data/docs/AUTH.md +1741 -0
  104. data/docs/CIRCUIT_BREAKER.md +224 -0
  105. data/docs/DEVELOPER_GUIDE.md +2664 -0
  106. data/docs/EVENT_SYSTEM.md +637 -0
  107. data/docs/EXECUTION_CONFIGURATION.md +341 -0
  108. data/docs/FLOW_CHART.md +149 -0
  109. data/docs/HEALTH.md +542 -0
  110. data/docs/METRICS.md +731 -0
  111. data/docs/OPTIMIZATION_PLAN.md +1479 -0
  112. data/docs/OVERVIEW.md +548 -0
  113. data/docs/QUICK_START.md +270 -0
  114. data/docs/REGISTRY_SYSTEMS.md +373 -0
  115. data/docs/REST_API.md +632 -0
  116. data/docs/REVERSIONING.md +404 -0
  117. data/docs/ROADMAP.md +221 -0
  118. data/docs/SQL_FUNCTIONS.md +1408 -0
  119. data/docs/TASK_EXECUTION_CONTROL_FLOW.md +237 -0
  120. data/docs/TELEMETRY.md +795 -0
  121. data/docs/TROUBLESHOOTING.md +756 -0
  122. data/docs/TaskHandlerGenerator.html +255 -0
  123. data/docs/Tasker/Analysis/RuntimeGraphAnalyzer.html +907 -0
  124. data/docs/Tasker/Analysis/TemplateGraphAnalyzer.html +1236 -0
  125. data/docs/Tasker/Analysis.html +117 -0
  126. data/docs/Tasker/AnalyticsController.html +450 -0
  127. data/docs/Tasker/AnalyticsService/BottleneckAnalytics.html +816 -0
  128. data/docs/Tasker/AnalyticsService/PerformanceAnalytics.html +586 -0
  129. data/docs/Tasker/AnalyticsService.html +2221 -0
  130. data/docs/Tasker/AnnotationType.html +137 -0
  131. data/docs/Tasker/AnnotationTypeSerializer.html +124 -0
  132. data/docs/Tasker/ApplicationController.html +147 -0
  133. data/docs/Tasker/ApplicationJob.html +128 -0
  134. data/docs/Tasker/ApplicationRecord.html +378 -0
  135. data/docs/Tasker/Authentication/AuthenticationError.html +124 -0
  136. data/docs/Tasker/Authentication/ConfigurationError.html +124 -0
  137. data/docs/Tasker/Authentication/Coordinator.html +242 -0
  138. data/docs/Tasker/Authentication/Interface.html +560 -0
  139. data/docs/Tasker/Authentication/InterfaceError.html +124 -0
  140. data/docs/Tasker/Authentication/NoneAuthenticator.html +338 -0
  141. data/docs/Tasker/Authentication.html +119 -0
  142. data/docs/Tasker/Authorization/AuthorizationError.html +139 -0
  143. data/docs/Tasker/Authorization/BaseCoordinator.html +927 -0
  144. data/docs/Tasker/Authorization/ConfigurationError.html +153 -0
  145. data/docs/Tasker/Authorization/ResourceConstants/ACTIONS.html +428 -0
  146. data/docs/Tasker/Authorization/ResourceConstants/RESOURCES.html +360 -0
  147. data/docs/Tasker/Authorization/ResourceConstants.html +146 -0
  148. data/docs/Tasker/Authorization/ResourceRegistry.html +875 -0
  149. data/docs/Tasker/Authorization/UnauthorizedError.html +153 -0
  150. data/docs/Tasker/Authorization.html +582 -0
  151. data/docs/Tasker/CacheCapabilities.html +167 -0
  152. data/docs/Tasker/CacheStrategy.html +1297 -0
  153. data/docs/Tasker/Concerns/Authenticatable.html +116 -0
  154. data/docs/Tasker/Concerns/Authorizable/AdminStatusChecker.html +256 -0
  155. data/docs/Tasker/Concerns/Authorizable.html +816 -0
  156. data/docs/Tasker/Concerns/ControllerAuthorizable.html +157 -0
  157. data/docs/Tasker/Concerns/EventPublisher.html +4023 -0
  158. data/docs/Tasker/Concerns/IdempotentStateTransitions.html +806 -0
  159. data/docs/Tasker/Concerns/LifecycleEventHelpers.html +129 -0
  160. data/docs/Tasker/Concerns/OrchestrationPublisher.html +129 -0
  161. data/docs/Tasker/Concerns/StateMachineBase/ClassMethods.html +1075 -0
  162. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase/ClassMethods.html +191 -0
  163. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase.html +126 -0
  164. data/docs/Tasker/Concerns/StateMachineBase.html +153 -0
  165. data/docs/Tasker/Concerns/StructuredLogging.html +1413 -0
  166. data/docs/Tasker/Concerns.html +117 -0
  167. data/docs/Tasker/Configuration/AuthConfiguration.html +1023 -0
  168. data/docs/Tasker/Configuration/ConfigurationProxy.html +581 -0
  169. data/docs/Tasker/Configuration/DatabaseConfiguration.html +475 -0
  170. data/docs/Tasker/Configuration/EngineConfiguration.html +1265 -0
  171. data/docs/Tasker/Configuration/HealthConfiguration.html +791 -0
  172. data/docs/Tasker/Configuration/TelemetryConfiguration.html +1308 -0
  173. data/docs/Tasker/Configuration/TelemetryConfigurationProxy.html +388 -0
  174. data/docs/Tasker/Configuration.html +1669 -0
  175. data/docs/Tasker/ConfigurationError.html +143 -0
  176. data/docs/Tasker/ConfiguredTask.html +514 -0
  177. data/docs/Tasker/Constants/EventDefinitions.html +590 -0
  178. data/docs/Tasker/Constants/LifecycleEvents.html +137 -0
  179. data/docs/Tasker/Constants/ObservabilityEvents/Step.html +152 -0
  180. data/docs/Tasker/Constants/ObservabilityEvents/Task.html +142 -0
  181. data/docs/Tasker/Constants/ObservabilityEvents.html +126 -0
  182. data/docs/Tasker/Constants/RegistryEvents.html +285 -0
  183. data/docs/Tasker/Constants/StepEvents.html +177 -0
  184. data/docs/Tasker/Constants/TaskEvents.html +167 -0
  185. data/docs/Tasker/Constants/TaskExecution/ExecutionStatus.html +207 -0
  186. data/docs/Tasker/Constants/TaskExecution/HealthStatus.html +191 -0
  187. data/docs/Tasker/Constants/TaskExecution/RecommendedAction.html +207 -0
  188. data/docs/Tasker/Constants/TaskExecution.html +126 -0
  189. data/docs/Tasker/Constants/TaskFinalization/ErrorMessages.html +132 -0
  190. data/docs/Tasker/Constants/TaskFinalization/PendingReasons.html +207 -0
  191. data/docs/Tasker/Constants/TaskFinalization/ReenqueueReasons.html +239 -0
  192. data/docs/Tasker/Constants/TaskFinalization.html +126 -0
  193. data/docs/Tasker/Constants/TaskStatuses.html +223 -0
  194. data/docs/Tasker/Constants/TestEvents.html +163 -0
  195. data/docs/Tasker/Constants/WorkflowEvents.html +222 -0
  196. data/docs/Tasker/Constants/WorkflowStepStatuses.html +223 -0
  197. data/docs/Tasker/Constants.html +561 -0
  198. data/docs/Tasker/DependentSystem.html +137 -0
  199. data/docs/Tasker/DependentSystemObjectMap.html +250 -0
  200. data/docs/Tasker/DetectorRegistry.html +598 -0
  201. data/docs/Tasker/Diagram/Edge.html +1191 -0
  202. data/docs/Tasker/Diagram/Flowchart.html +1539 -0
  203. data/docs/Tasker/Diagram/Node.html +1165 -0
  204. data/docs/Tasker/Diagram.html +117 -0
  205. data/docs/Tasker/Engine.html +215 -0
  206. data/docs/Tasker/Error.html +139 -0
  207. data/docs/Tasker/Events/Bus.html +1226 -0
  208. data/docs/Tasker/Events/Catalog/CatalogPrinter.html +258 -0
  209. data/docs/Tasker/Events/Catalog/CustomEventRegistrar.html +276 -0
  210. data/docs/Tasker/Events/Catalog/ExamplePayloadGenerator.html +294 -0
  211. data/docs/Tasker/Events/Catalog.html +1291 -0
  212. data/docs/Tasker/Events/CustomRegistry.html +943 -0
  213. data/docs/Tasker/Events/DefinitionLoader.html +575 -0
  214. data/docs/Tasker/Events/EventPayloadBuilder/ErrorInfoExtractor.html +286 -0
  215. data/docs/Tasker/Events/EventPayloadBuilder/StepPayloadBuilder.html +312 -0
  216. data/docs/Tasker/Events/EventPayloadBuilder.html +664 -0
  217. data/docs/Tasker/Events/Publisher.html +365 -0
  218. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer/ErrorTypeClassifier.html +1128 -0
  219. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer.html +270 -0
  220. data/docs/Tasker/Events/Subscribers/BaseSubscriber/MetricTagsExtractor.html +266 -0
  221. data/docs/Tasker/Events/Subscribers/BaseSubscriber.html +2556 -0
  222. data/docs/Tasker/Events/Subscribers/MetricsSubscriber.html +723 -0
  223. data/docs/Tasker/Events/Subscribers/TelemetrySubscriber.html +2251 -0
  224. data/docs/Tasker/Events/Subscribers.html +117 -0
  225. data/docs/Tasker/Events/SubscriptionLoader.html +493 -0
  226. data/docs/Tasker/Events.html +294 -0
  227. data/docs/Tasker/EventsGenerator.html +459 -0
  228. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics/AnalyticsMetrics.html +135 -0
  229. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics.html +412 -0
  230. data/docs/Tasker/Functions/FunctionBasedDependencyLevels.html +598 -0
  231. data/docs/Tasker/Functions/FunctionBasedSlowestSteps/SlowestStep.html +135 -0
  232. data/docs/Tasker/Functions/FunctionBasedSlowestSteps.html +453 -0
  233. data/docs/Tasker/Functions/FunctionBasedSlowestTasks/SlowestTask.html +135 -0
  234. data/docs/Tasker/Functions/FunctionBasedSlowestTasks.html +453 -0
  235. data/docs/Tasker/Functions/FunctionBasedStepReadinessStatus.html +1457 -0
  236. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts/HealthMetrics.html +135 -0
  237. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts.html +370 -0
  238. data/docs/Tasker/Functions/FunctionBasedTaskExecutionContext.html +1250 -0
  239. data/docs/Tasker/Functions/FunctionWrapper.html +479 -0
  240. data/docs/Tasker/Functions.html +117 -0
  241. data/docs/Tasker/Generators/AuthenticatorGenerator/UsageInstructionsFormatter.html +244 -0
  242. data/docs/Tasker/Generators/AuthenticatorGenerator.html +373 -0
  243. data/docs/Tasker/Generators/AuthorizationCoordinatorGenerator.html +430 -0
  244. data/docs/Tasker/Generators/SubscriberGenerator.html +377 -0
  245. data/docs/Tasker/Generators/TaskHandlerGenerator.html +263 -0
  246. data/docs/Tasker/Generators.html +117 -0
  247. data/docs/Tasker/GraphQLTypes/AnnotationType.html +132 -0
  248. data/docs/Tasker/GraphQLTypes/BaseArgument.html +124 -0
  249. data/docs/Tasker/GraphQLTypes/BaseConnection.html +124 -0
  250. data/docs/Tasker/GraphQLTypes/BaseEdge.html +130 -0
  251. data/docs/Tasker/GraphQLTypes/BaseEnum.html +124 -0
  252. data/docs/Tasker/GraphQLTypes/BaseField.html +124 -0
  253. data/docs/Tasker/GraphQLTypes/BaseInputObject.html +124 -0
  254. data/docs/Tasker/GraphQLTypes/BaseInterface.html +116 -0
  255. data/docs/Tasker/GraphQLTypes/BaseObject.html +128 -0
  256. data/docs/Tasker/GraphQLTypes/BaseScalar.html +124 -0
  257. data/docs/Tasker/GraphQLTypes/BaseUnion.html +124 -0
  258. data/docs/Tasker/GraphQLTypes/DependentSystemObjectMapType.html +132 -0
  259. data/docs/Tasker/GraphQLTypes/DependentSystemType.html +132 -0
  260. data/docs/Tasker/GraphQLTypes/MutationType.html +132 -0
  261. data/docs/Tasker/GraphQLTypes/NamedStepType.html +132 -0
  262. data/docs/Tasker/GraphQLTypes/NamedTaskType.html +132 -0
  263. data/docs/Tasker/GraphQLTypes/NamedTasksNamedStepType.html +132 -0
  264. data/docs/Tasker/GraphQLTypes/NodeType.html +118 -0
  265. data/docs/Tasker/GraphQLTypes/QueryType.html +139 -0
  266. data/docs/Tasker/GraphQLTypes/TaskAnnotationType.html +132 -0
  267. data/docs/Tasker/GraphQLTypes/TaskInterface.html +111 -0
  268. data/docs/Tasker/GraphQLTypes/TaskType.html +201 -0
  269. data/docs/Tasker/GraphQLTypes/WorkflowStepType.html +694 -0
  270. data/docs/Tasker/GraphQLTypes.html +130 -0
  271. data/docs/Tasker/GraphqlController.html +251 -0
  272. data/docs/Tasker/HandlerFactory.html +1528 -0
  273. data/docs/Tasker/HandlerSerializer.html +682 -0
  274. data/docs/Tasker/HandlersController.html +574 -0
  275. data/docs/Tasker/HashIdentityStrategy.html +278 -0
  276. data/docs/Tasker/Health/ReadinessChecker.html +712 -0
  277. data/docs/Tasker/Health/StatusChecker.html +653 -0
  278. data/docs/Tasker/Health.html +117 -0
  279. data/docs/Tasker/HealthController.html +523 -0
  280. data/docs/Tasker/IdentityStrategy.html +276 -0
  281. data/docs/Tasker/InvalidTaskHandlerConfig.html +135 -0
  282. data/docs/Tasker/LifecycleEvents/Events/Step.html +162 -0
  283. data/docs/Tasker/LifecycleEvents/Events/Task.html +162 -0
  284. data/docs/Tasker/LifecycleEvents/Events.html +204 -0
  285. data/docs/Tasker/LifecycleEvents/Publisher.html +132 -0
  286. data/docs/Tasker/LifecycleEvents.html +799 -0
  287. data/docs/Tasker/Logging/CorrelationIdGenerator.html +688 -0
  288. data/docs/Tasker/Logging.html +115 -0
  289. data/docs/Tasker/MetricsController.html +293 -0
  290. data/docs/Tasker/MetricsExportJob.html +414 -0
  291. data/docs/Tasker/Mutations/BaseMutation.html +128 -0
  292. data/docs/Tasker/Mutations/CancelStep.html +219 -0
  293. data/docs/Tasker/Mutations/CancelTask.html +221 -0
  294. data/docs/Tasker/Mutations/CreateTask.html +243 -0
  295. data/docs/Tasker/Mutations/UpdateStep.html +243 -0
  296. data/docs/Tasker/Mutations/UpdateTask.html +243 -0
  297. data/docs/Tasker/Mutations.html +117 -0
  298. data/docs/Tasker/NamedStep.html +216 -0
  299. data/docs/Tasker/NamedTask.html +910 -0
  300. data/docs/Tasker/NamedTasksNamedStep.html +435 -0
  301. data/docs/Tasker/Orchestration/BackoffCalculator.html +404 -0
  302. data/docs/Tasker/Orchestration/ConnectionBuilder/ConfigValidator.html +258 -0
  303. data/docs/Tasker/Orchestration/ConnectionBuilder.html +435 -0
  304. data/docs/Tasker/Orchestration/ConnectionPoolIntelligence.html +513 -0
  305. data/docs/Tasker/Orchestration/Coordinator.html +641 -0
  306. data/docs/Tasker/Orchestration/FutureStateAnalyzer.html +1045 -0
  307. data/docs/Tasker/Orchestration/Orchestrator.html +679 -0
  308. data/docs/Tasker/Orchestration/PluginIntegration.html +1127 -0
  309. data/docs/Tasker/Orchestration/ResponseProcessor.html +504 -0
  310. data/docs/Tasker/Orchestration/RetryHeaderParser.html +304 -0
  311. data/docs/Tasker/Orchestration/StepExecutor.html +995 -0
  312. data/docs/Tasker/Orchestration/StepSequenceFactory.html +644 -0
  313. data/docs/Tasker/Orchestration/TaskFinalizer/BlockageChecker.html +264 -0
  314. data/docs/Tasker/Orchestration/TaskFinalizer/ContextManager.html +254 -0
  315. data/docs/Tasker/Orchestration/TaskFinalizer/DelayCalculator.html +556 -0
  316. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationDecisionMaker.html +348 -0
  317. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationProcessor.html +286 -0
  318. data/docs/Tasker/Orchestration/TaskFinalizer/ReasonDeterminer.html +432 -0
  319. data/docs/Tasker/Orchestration/TaskFinalizer/ReenqueueManager.html +296 -0
  320. data/docs/Tasker/Orchestration/TaskFinalizer/UnclearStateHandler.html +314 -0
  321. data/docs/Tasker/Orchestration/TaskFinalizer.html +1212 -0
  322. data/docs/Tasker/Orchestration/TaskInitializer.html +766 -0
  323. data/docs/Tasker/Orchestration/TaskReenqueuer.html +506 -0
  324. data/docs/Tasker/Orchestration/ViableStepDiscovery.html +442 -0
  325. data/docs/Tasker/Orchestration/WorkflowCoordinator.html +510 -0
  326. data/docs/Tasker/Orchestration.html +130 -0
  327. data/docs/Tasker/PageSort/PageSortParamsBuilder.html +296 -0
  328. data/docs/Tasker/PageSort.html +247 -0
  329. data/docs/Tasker/PermanentError.html +518 -0
  330. data/docs/Tasker/ProceduralError.html +147 -0
  331. data/docs/Tasker/Queries/AllAnnotationTypes.html +217 -0
  332. data/docs/Tasker/Queries/AllTasks.html +221 -0
  333. data/docs/Tasker/Queries/BaseQuery.html +128 -0
  334. data/docs/Tasker/Queries/Helpers.html +187 -0
  335. data/docs/Tasker/Queries/OneStep.html +225 -0
  336. data/docs/Tasker/Queries/OneTask.html +217 -0
  337. data/docs/Tasker/Queries/TasksByAnnotation.html +231 -0
  338. data/docs/Tasker/Queries/TasksByStatus.html +233 -0
  339. data/docs/Tasker/Queries.html +119 -0
  340. data/docs/Tasker/Railtie.html +124 -0
  341. data/docs/Tasker/Registry/BaseRegistry.html +1690 -0
  342. data/docs/Tasker/Registry/EventPublisher.html +667 -0
  343. data/docs/Tasker/Registry/InterfaceValidator.html +569 -0
  344. data/docs/Tasker/Registry/RegistrationError.html +132 -0
  345. data/docs/Tasker/Registry/RegistryError.html +139 -0
  346. data/docs/Tasker/Registry/StatisticsCollector.html +841 -0
  347. data/docs/Tasker/Registry/SubscriberRegistry.html +1504 -0
  348. data/docs/Tasker/Registry/ValidationError.html +132 -0
  349. data/docs/Tasker/Registry.html +119 -0
  350. data/docs/Tasker/RetryableError.html +515 -0
  351. data/docs/Tasker/StateMachine/Compatibility.html +282 -0
  352. data/docs/Tasker/StateMachine/InvalidStateTransition.html +135 -0
  353. data/docs/Tasker/StateMachine/StepStateMachine/StandardizedPayloadBuilder.html +260 -0
  354. data/docs/Tasker/StateMachine/StepStateMachine.html +2215 -0
  355. data/docs/Tasker/StateMachine/TaskStateMachine.html +734 -0
  356. data/docs/Tasker/StateMachine.html +602 -0
  357. data/docs/Tasker/StepDagRelationship.html +657 -0
  358. data/docs/Tasker/StepHandler/Api/Config.html +1091 -0
  359. data/docs/Tasker/StepHandler/Api.html +884 -0
  360. data/docs/Tasker/StepHandler/AutomaticEventPublishing.html +321 -0
  361. data/docs/Tasker/StepHandler/Base.html +970 -0
  362. data/docs/Tasker/StepHandler.html +119 -0
  363. data/docs/Tasker/StepReadinessStatus.html +836 -0
  364. data/docs/Tasker/Task.html +2478 -0
  365. data/docs/Tasker/TaskAnnotation.html +137 -0
  366. data/docs/Tasker/TaskAnnotationSerializer.html +124 -0
  367. data/docs/Tasker/TaskBuilder/StepNameValidator.html +264 -0
  368. data/docs/Tasker/TaskBuilder/StepTemplateDefiner.html +264 -0
  369. data/docs/Tasker/TaskBuilder.html +764 -0
  370. data/docs/Tasker/TaskDiagram/StepToStepEdgeBuilder.html +260 -0
  371. data/docs/Tasker/TaskDiagram/TaskToRootStepEdgeBuilder.html +290 -0
  372. data/docs/Tasker/TaskDiagram.html +548 -0
  373. data/docs/Tasker/TaskDiagramsController.html +240 -0
  374. data/docs/Tasker/TaskExecutionContext.html +469 -0
  375. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/ClassBasedEventRegistrar.html +238 -0
  376. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/YamlEventRegistrar.html +254 -0
  377. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner.html +988 -0
  378. data/docs/Tasker/TaskHandler/ClassMethods.html +395 -0
  379. data/docs/Tasker/TaskHandler/InstanceMethods.html +1396 -0
  380. data/docs/Tasker/TaskHandler/StepGroup.html +1748 -0
  381. data/docs/Tasker/TaskHandler.html +271 -0
  382. data/docs/Tasker/TaskNamespace.html +312 -0
  383. data/docs/Tasker/TaskRunnerJob.html +406 -0
  384. data/docs/Tasker/TaskSerializer.html +474 -0
  385. data/docs/Tasker/TaskTransition.html +1517 -0
  386. data/docs/Tasker/TaskWorkflowSummary.html +988 -0
  387. data/docs/Tasker/TaskerRailsSchema/InvalidObjectTypeError.html +132 -0
  388. data/docs/Tasker/TaskerRailsSchema/TypeResolutionError.html +139 -0
  389. data/docs/Tasker/TaskerRailsSchema/UnknownInterfaceError.html +132 -0
  390. data/docs/Tasker/TaskerRailsSchema.html +384 -0
  391. data/docs/Tasker/TasksController.html +595 -0
  392. data/docs/Tasker/Telemetry/EventMapping.html +1307 -0
  393. data/docs/Tasker/Telemetry/EventRouter.html +2178 -0
  394. data/docs/Tasker/Telemetry/Events/ExportEvents.html +246 -0
  395. data/docs/Tasker/Telemetry/Events.html +115 -0
  396. data/docs/Tasker/Telemetry/ExportCoordinator/DistributedLockTimeoutError.html +135 -0
  397. data/docs/Tasker/Telemetry/ExportCoordinator.html +2137 -0
  398. data/docs/Tasker/Telemetry/IntelligentCacheManager.html +1083 -0
  399. data/docs/Tasker/Telemetry/LogBackend.html +1088 -0
  400. data/docs/Tasker/Telemetry/MetricTypes/Counter.html +1054 -0
  401. data/docs/Tasker/Telemetry/MetricTypes/Gauge.html +1270 -0
  402. data/docs/Tasker/Telemetry/MetricTypes/Histogram.html +1492 -0
  403. data/docs/Tasker/Telemetry/MetricTypes.html +153 -0
  404. data/docs/Tasker/Telemetry/MetricsBackend.html +2510 -0
  405. data/docs/Tasker/Telemetry/MetricsExportService.html +578 -0
  406. data/docs/Tasker/Telemetry/PluginRegistry.html +1774 -0
  407. data/docs/Tasker/Telemetry/Plugins/BaseExporter.html +1835 -0
  408. data/docs/Tasker/Telemetry/Plugins/CsvExporter.html +768 -0
  409. data/docs/Tasker/Telemetry/Plugins/JsonExporter.html +747 -0
  410. data/docs/Tasker/Telemetry/Plugins.html +117 -0
  411. data/docs/Tasker/Telemetry/PrometheusExporter.html +481 -0
  412. data/docs/Tasker/Telemetry/TraceBackend.html +891 -0
  413. data/docs/Tasker/Telemetry.html +130 -0
  414. data/docs/Tasker/Types/AuthConfig.html +886 -0
  415. data/docs/Tasker/Types/BackoffConfig.html +1063 -0
  416. data/docs/Tasker/Types/BaseConfig.html +227 -0
  417. data/docs/Tasker/Types/CacheConfig.html +1731 -0
  418. data/docs/Tasker/Types/DatabaseConfig.html +388 -0
  419. data/docs/Tasker/Types/DependencyGraph.html +526 -0
  420. data/docs/Tasker/Types/DependencyGraphConfig.html +753 -0
  421. data/docs/Tasker/Types/EngineConfig.html +1181 -0
  422. data/docs/Tasker/Types/ExecutionConfig.html +1963 -0
  423. data/docs/Tasker/Types/GraphEdge.html +517 -0
  424. data/docs/Tasker/Types/GraphMetadata.html +781 -0
  425. data/docs/Tasker/Types/GraphNode.html +694 -0
  426. data/docs/Tasker/Types/HealthConfig.html +784 -0
  427. data/docs/Tasker/Types/StepSequence.html +353 -0
  428. data/docs/Tasker/Types/StepTemplate.html +1193 -0
  429. data/docs/Tasker/Types/TaskRequest.html +1179 -0
  430. data/docs/Tasker/Types/TelemetryConfig.html +2746 -0
  431. data/docs/Tasker/Types.html +154 -0
  432. data/docs/Tasker/WorkflowStep/StepFinder.html +282 -0
  433. data/docs/Tasker/WorkflowStep.html +2724 -0
  434. data/docs/Tasker/WorkflowStepEdge.html +306 -0
  435. data/docs/Tasker/WorkflowStepSerializer.html +305 -0
  436. data/docs/Tasker/WorkflowStepTransition/TransitionDescriptionFormatter.html +282 -0
  437. data/docs/Tasker/WorkflowStepTransition.html +2201 -0
  438. data/docs/Tasker/WorkflowStepsController.html +462 -0
  439. data/docs/Tasker.html +468 -0
  440. data/docs/VISION.md +584 -0
  441. data/docs/WHY.md +21 -0
  442. data/docs/_index.html +2319 -0
  443. data/docs/class_list.html +54 -0
  444. data/docs/css/common.css +1 -0
  445. data/docs/css/full_list.css +58 -0
  446. data/docs/css/style.css +503 -0
  447. data/docs/events/migration_plan_outcomes.md +80 -0
  448. data/docs/file.README.html +537 -0
  449. data/docs/file_list.html +59 -0
  450. data/docs/frames.html +22 -0
  451. data/docs/index.html +537 -0
  452. data/docs/js/app.js +344 -0
  453. data/docs/js/full_list.js +242 -0
  454. data/docs/js/jquery.js +4 -0
  455. data/docs/method_list.html +8854 -0
  456. data/docs/top-level-namespace.html +110 -0
  457. data/lib/generators/tasker/authenticator_generator.rb +301 -0
  458. data/lib/generators/tasker/authorization_coordinator_generator.rb +139 -0
  459. data/lib/generators/tasker/events_generator.rb +91 -0
  460. data/lib/generators/tasker/subscriber_generator.rb +107 -0
  461. data/lib/generators/tasker/task_handler_generator.rb +138 -0
  462. data/lib/generators/tasker/templates/api_token_authenticator.rb.erb +113 -0
  463. data/lib/generators/tasker/templates/api_token_authenticator_spec.rb.erb +144 -0
  464. data/lib/generators/tasker/templates/authorization_coordinator.rb.erb +82 -0
  465. data/lib/generators/tasker/templates/authorization_coordinator_spec.rb.erb +136 -0
  466. data/lib/generators/tasker/templates/custom_authenticator.rb.erb +108 -0
  467. data/lib/generators/tasker/templates/custom_authenticator_spec.rb.erb +162 -0
  468. data/lib/generators/tasker/templates/custom_events.yml.erb +62 -0
  469. data/lib/generators/tasker/templates/custom_subscriber.rb.erb +72 -0
  470. data/lib/generators/tasker/templates/devise_authenticator.rb.erb +101 -0
  471. data/lib/generators/tasker/templates/devise_authenticator_spec.rb.erb +126 -0
  472. data/lib/generators/tasker/templates/initialize.rb.erb +202 -0
  473. data/lib/generators/tasker/templates/jwt_authenticator.rb.erb +144 -0
  474. data/lib/generators/tasker/templates/jwt_authenticator_spec.rb.erb +298 -0
  475. data/lib/generators/tasker/templates/metrics_subscriber.rb.erb +258 -0
  476. data/lib/generators/tasker/templates/metrics_subscriber_spec.rb.erb +308 -0
  477. data/lib/generators/tasker/templates/omniauth_authenticator.rb.erb +135 -0
  478. data/lib/generators/tasker/templates/omniauth_authenticator_spec.rb.erb +196 -0
  479. data/lib/generators/tasker/templates/opentelemetry_initializer.rb +52 -0
  480. data/lib/generators/tasker/templates/subscriber.rb.erb +64 -0
  481. data/lib/generators/tasker/templates/subscriber_spec.rb.erb +80 -0
  482. data/lib/generators/tasker/templates/task_config.yaml.erb +117 -0
  483. data/lib/generators/tasker/templates/task_handler.rb.erb +60 -0
  484. data/lib/generators/tasker/templates/task_handler_spec.rb.erb +165 -0
  485. data/lib/tasker/analysis/runtime_graph_analyzer.rb +1168 -0
  486. data/lib/tasker/analysis/template_graph_analyzer.rb +328 -0
  487. data/lib/tasker/authentication/coordinator.rb +78 -0
  488. data/lib/tasker/authentication/errors.rb +9 -0
  489. data/lib/tasker/authentication/interface.rb +36 -0
  490. data/lib/tasker/authentication/none_authenticator.rb +26 -0
  491. data/lib/tasker/authorization/base_coordinator.rb +112 -0
  492. data/lib/tasker/authorization/errors.rb +26 -0
  493. data/lib/tasker/authorization/resource_constants.rb +73 -0
  494. data/lib/tasker/authorization/resource_registry.rb +136 -0
  495. data/lib/tasker/authorization.rb +75 -0
  496. data/lib/tasker/cache_capabilities.rb +131 -0
  497. data/lib/tasker/cache_strategy.rb +469 -0
  498. data/lib/tasker/concerns/authenticatable.rb +41 -0
  499. data/lib/tasker/concerns/authorizable.rb +204 -0
  500. data/lib/tasker/concerns/controller_authorizable.rb +124 -0
  501. data/lib/tasker/concerns/event_publisher.rb +716 -0
  502. data/lib/tasker/concerns/idempotent_state_transitions.rb +128 -0
  503. data/lib/tasker/concerns/state_machine_base.rb +218 -0
  504. data/lib/tasker/concerns/structured_logging.rb +387 -0
  505. data/lib/tasker/configuration.rb +325 -0
  506. data/lib/tasker/constants/event_definitions.rb +147 -0
  507. data/lib/tasker/constants/registry_events.rb +54 -0
  508. data/lib/tasker/constants.rb +417 -0
  509. data/lib/tasker/engine.rb +90 -0
  510. data/lib/tasker/errors.rb +90 -0
  511. data/lib/tasker/events/catalog.rb +432 -0
  512. data/lib/tasker/events/custom_registry.rb +175 -0
  513. data/lib/tasker/events/definition_loader.rb +199 -0
  514. data/lib/tasker/events/event_payload_builder.rb +461 -0
  515. data/lib/tasker/events/publisher.rb +149 -0
  516. data/lib/tasker/events/subscribers/base_subscriber.rb +601 -0
  517. data/lib/tasker/events/subscribers/metrics_subscriber.rb +120 -0
  518. data/lib/tasker/events/subscribers/telemetry_subscriber.rb +462 -0
  519. data/lib/tasker/events/subscription_loader.rb +161 -0
  520. data/lib/tasker/events.rb +37 -0
  521. data/lib/tasker/functions/function_based_analytics_metrics.rb +103 -0
  522. data/lib/tasker/functions/function_based_dependency_levels.rb +54 -0
  523. data/lib/tasker/functions/function_based_slowest_steps.rb +84 -0
  524. data/lib/tasker/functions/function_based_slowest_tasks.rb +84 -0
  525. data/lib/tasker/functions/function_based_step_readiness_status.rb +183 -0
  526. data/lib/tasker/functions/function_based_system_health_counts.rb +94 -0
  527. data/lib/tasker/functions/function_based_task_execution_context.rb +148 -0
  528. data/lib/tasker/functions/function_wrapper.rb +42 -0
  529. data/lib/tasker/functions.rb +12 -0
  530. data/lib/tasker/handler_factory.rb +327 -0
  531. data/lib/tasker/health/readiness_checker.rb +186 -0
  532. data/lib/tasker/health/status_checker.rb +203 -0
  533. data/lib/tasker/identity_strategy.rb +38 -0
  534. data/lib/tasker/logging/correlation_id_generator.rb +120 -0
  535. data/lib/tasker/orchestration/backoff_calculator.rb +184 -0
  536. data/lib/tasker/orchestration/connection_builder.rb +122 -0
  537. data/lib/tasker/orchestration/connection_pool_intelligence.rb +177 -0
  538. data/lib/tasker/orchestration/coordinator.rb +119 -0
  539. data/lib/tasker/orchestration/future_state_analyzer.rb +137 -0
  540. data/lib/tasker/orchestration/plugin_integration.rb +124 -0
  541. data/lib/tasker/orchestration/response_processor.rb +168 -0
  542. data/lib/tasker/orchestration/retry_header_parser.rb +78 -0
  543. data/lib/tasker/orchestration/step_executor.rb +941 -0
  544. data/lib/tasker/orchestration/step_sequence_factory.rb +67 -0
  545. data/lib/tasker/orchestration/task_finalizer.rb +564 -0
  546. data/lib/tasker/orchestration/task_initializer.rb +140 -0
  547. data/lib/tasker/orchestration/task_reenqueuer.rb +71 -0
  548. data/lib/tasker/orchestration/viable_step_discovery.rb +65 -0
  549. data/lib/tasker/orchestration/workflow_coordinator.rb +294 -0
  550. data/lib/tasker/orchestration.rb +45 -0
  551. data/lib/tasker/railtie.rb +9 -0
  552. data/lib/tasker/registry/base_registry.rb +177 -0
  553. data/lib/tasker/registry/event_publisher.rb +91 -0
  554. data/lib/tasker/registry/interface_validator.rb +140 -0
  555. data/lib/tasker/registry/statistics_collector.rb +381 -0
  556. data/lib/tasker/registry/subscriber_registry.rb +285 -0
  557. data/lib/tasker/registry.rb +22 -0
  558. data/lib/tasker/state_machine/step_state_machine.rb +508 -0
  559. data/lib/tasker/state_machine/task_state_machine.rb +192 -0
  560. data/lib/tasker/state_machine.rb +83 -0
  561. data/lib/tasker/step_handler/api.rb +410 -0
  562. data/lib/tasker/step_handler/base.rb +206 -0
  563. data/lib/tasker/task_builder.rb +432 -0
  564. data/lib/tasker/task_handler/class_methods.rb +327 -0
  565. data/lib/tasker/task_handler/instance_methods.rb +293 -0
  566. data/lib/tasker/task_handler/step_group.rb +182 -0
  567. data/lib/tasker/task_handler.rb +43 -0
  568. data/lib/tasker/telemetry/event_mapping.rb +126 -0
  569. data/lib/tasker/telemetry/event_router.rb +318 -0
  570. data/lib/tasker/telemetry/events/export_events.rb +38 -0
  571. data/lib/tasker/telemetry/export_coordinator.rb +497 -0
  572. data/lib/tasker/telemetry/intelligent_cache_manager.rb +508 -0
  573. data/lib/tasker/telemetry/log_backend.rb +224 -0
  574. data/lib/tasker/telemetry/metric_types.rb +416 -0
  575. data/lib/tasker/telemetry/metrics_backend.rb +1227 -0
  576. data/lib/tasker/telemetry/metrics_export_service.rb +392 -0
  577. data/lib/tasker/telemetry/plugin_registry.rb +333 -0
  578. data/lib/tasker/telemetry/plugins/base_exporter.rb +246 -0
  579. data/lib/tasker/telemetry/plugins/csv_exporter.rb +198 -0
  580. data/lib/tasker/telemetry/plugins/json_exporter.rb +141 -0
  581. data/lib/tasker/telemetry/prometheus_exporter.rb +249 -0
  582. data/lib/tasker/telemetry/trace_backend.rb +186 -0
  583. data/lib/tasker/telemetry.rb +59 -0
  584. data/lib/tasker/types/auth_config.rb +81 -0
  585. data/lib/tasker/types/backoff_config.rb +142 -0
  586. data/lib/tasker/types/cache_config.rb +257 -0
  587. data/lib/tasker/types/database_config.rb +39 -0
  588. data/lib/tasker/types/dependency_graph.rb +225 -0
  589. data/lib/tasker/types/dependency_graph_config.rb +149 -0
  590. data/lib/tasker/types/engine_config.rb +131 -0
  591. data/lib/tasker/types/execution_config.rb +289 -0
  592. data/lib/tasker/types/health_config.rb +84 -0
  593. data/lib/tasker/types/step_sequence.rb +24 -0
  594. data/lib/tasker/types/step_template.rb +63 -0
  595. data/lib/tasker/types/task_request.rb +60 -0
  596. data/lib/tasker/types/telemetry_config.rb +273 -0
  597. data/lib/tasker/types.rb +64 -0
  598. data/lib/tasker/version.rb +8 -0
  599. data/lib/tasker.rb +82 -0
  600. data/lib/tasks/tasker_tasks.rake +383 -0
  601. metadata +954 -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,416 @@
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? ? '' : format_labels_for_description(labels)
104
+ "#{name}#{label_str} = #{value} (counter)"
105
+ end
106
+
107
+ private
108
+
109
+ # Format labels consistently across Ruby versions
110
+ # Uses the older Ruby hash syntax for backward compatibility with tests
111
+ #
112
+ # @param labels_hash [Hash] The labels hash to format
113
+ # @return [String] Formatted label string
114
+ def format_labels_for_description(labels_hash)
115
+ return '' if labels_hash.empty?
116
+
117
+ formatted_pairs = labels_hash.map do |key, value|
118
+ ":#{key}=>#{value.inspect}"
119
+ end
120
+ "{#{formatted_pairs.join(', ')}}"
121
+ end
122
+ end
123
+
124
+ # Gauge represents a metric value that can go up or down
125
+ #
126
+ # Gauges are thread-safe and support atomic set, increment, and decrement
127
+ # operations. They follow fail-fast principles with explicit validation.
128
+ #
129
+ # @example Production usage
130
+ # gauge = Gauge.new('active_connections')
131
+ # gauge.set(100) # Set to specific value
132
+ # gauge.increment(5) # +5 connections
133
+ # gauge.decrement(2) # -2 connections
134
+ # gauge.value # Get current value
135
+ #
136
+ class Gauge
137
+ # @return [String] The metric name
138
+ attr_reader :name
139
+
140
+ # @return [Hash] The metric labels for dimensional data
141
+ attr_reader :labels
142
+
143
+ # @return [Time] When this metric was first created
144
+ attr_reader :created_at
145
+
146
+ # Initialize a new gauge metric
147
+ #
148
+ # @param name [String] The metric name (must be present)
149
+ # @param labels [Hash] Optional labels for dimensional metrics
150
+ # @param initial_value [Numeric] Initial gauge value
151
+ # @raise [ArgumentError] If name is nil or empty, or initial_value is not numeric
152
+ def initialize(name, labels: {}, initial_value: 0)
153
+ raise ArgumentError, 'Metric name cannot be nil or empty' if name.nil? || name.strip.empty?
154
+
155
+ unless initial_value.is_a?(Numeric)
156
+ raise ArgumentError,
157
+ "Initial value must be numeric, got: #{initial_value.class}"
158
+ end
159
+
160
+ @name = name.to_s.freeze
161
+ @labels = labels.freeze
162
+ @value = Concurrent::AtomicReference.new(initial_value)
163
+ @created_at = Time.current.freeze
164
+ end
165
+
166
+ # Set the gauge to a specific value
167
+ #
168
+ # @param new_value [Numeric] The new gauge value
169
+ # @return [Numeric] The new gauge value
170
+ # @raise [ArgumentError] If new_value is not numeric
171
+ def set(new_value)
172
+ raise ArgumentError, "Gauge value must be numeric, got: #{new_value.class}" unless new_value.is_a?(Numeric)
173
+
174
+ @value.set(new_value)
175
+ new_value
176
+ end
177
+
178
+ # Increment the gauge by a specified amount
179
+ #
180
+ # @param amount [Numeric] Amount to increment (can be negative)
181
+ # @return [Numeric] The new gauge value
182
+ # @raise [ArgumentError] If amount is not numeric
183
+ def increment(amount = 1)
184
+ unless amount.is_a?(Numeric)
185
+ raise ArgumentError,
186
+ "Gauge increment amount must be numeric, got: #{amount.class}"
187
+ end
188
+
189
+ @value.update { |current| current + amount }
190
+ end
191
+
192
+ # Decrement the gauge by a specified amount
193
+ #
194
+ # @param amount [Numeric] Amount to decrement (must be positive)
195
+ # @return [Numeric] The new gauge value
196
+ # @raise [ArgumentError] If amount is not numeric or is negative
197
+ def decrement(amount = 1)
198
+ unless amount.is_a?(Numeric)
199
+ raise ArgumentError,
200
+ "Gauge decrement amount must be numeric, got: #{amount.class}"
201
+ end
202
+ raise ArgumentError, "Gauge decrement amount must be positive, got: #{amount}" if amount.negative?
203
+
204
+ increment(-amount)
205
+ end
206
+
207
+ # Get the current gauge value (thread-safe read)
208
+ #
209
+ # @return [Numeric] Current gauge value
210
+ def value
211
+ @value.get
212
+ end
213
+
214
+ # Get a hash representation of this metric
215
+ #
216
+ # @return [Hash] Metric data including name, labels, value, type
217
+ def to_h
218
+ {
219
+ name: name,
220
+ labels: labels,
221
+ value: value,
222
+ type: :gauge,
223
+ created_at: created_at
224
+ }
225
+ end
226
+
227
+ # Get a description of this metric for debugging
228
+ #
229
+ # @return [String] Human-readable description
230
+ def description
231
+ label_str = labels.empty? ? '' : format_labels_for_description(labels)
232
+ "#{name}#{label_str} = #{value} (gauge)"
233
+ end
234
+
235
+ private
236
+
237
+ # Format labels consistently across Ruby versions
238
+ # Uses the older Ruby hash syntax for backward compatibility with tests
239
+ #
240
+ # @param labels_hash [Hash] The labels hash to format
241
+ # @return [String] Formatted label string
242
+ def format_labels_for_description(labels_hash)
243
+ return '' if labels_hash.empty?
244
+
245
+ formatted_pairs = labels_hash.map do |key, value|
246
+ ":#{key}=>#{value.inspect}"
247
+ end
248
+ "{#{formatted_pairs.join(', ')}}"
249
+ end
250
+ end
251
+
252
+ # Histogram represents a metric that samples observations and counts them in buckets
253
+ #
254
+ # Histograms are thread-safe and provide statistical analysis of observed values.
255
+ # They track count, sum, and bucket distributions for duration and size metrics.
256
+ #
257
+ # @example Production usage
258
+ # histogram = Histogram.new('task_duration_seconds', buckets: [0.1, 0.5, 1.0, 5.0])
259
+ # histogram.observe(0.45) # Record a 0.45 second duration
260
+ # histogram.observe(2.1) # Record a 2.1 second duration
261
+ # histogram.count # Total observations
262
+ # histogram.sum # Sum of all observed values
263
+ # histogram.buckets # Bucket counts
264
+ #
265
+ class Histogram
266
+ # @return [String] The metric name
267
+ attr_reader :name
268
+
269
+ # @return [Hash] The metric labels for dimensional data
270
+ attr_reader :labels
271
+
272
+ # @return [Array<Numeric>] The histogram bucket boundaries
273
+ attr_reader :bucket_boundaries
274
+
275
+ # @return [Time] When this metric was first created
276
+ attr_reader :created_at
277
+
278
+ # Default bucket boundaries for duration metrics (in seconds)
279
+ 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
280
+
281
+ # Initialize a new histogram metric
282
+ #
283
+ # @param name [String] The metric name (must be present)
284
+ # @param labels [Hash] Optional labels for dimensional metrics
285
+ # @param buckets [Array<Numeric>] Bucket boundaries (must be sorted ascending)
286
+ # @raise [ArgumentError] If name is nil/empty or buckets are invalid
287
+ def initialize(name, labels: {}, buckets: DEFAULT_BUCKETS)
288
+ raise ArgumentError, 'Metric name cannot be nil or empty' if name.nil? || name.strip.empty?
289
+ raise ArgumentError, 'Buckets must be an array' unless buckets.is_a?(Array)
290
+ raise ArgumentError, 'Buckets cannot be empty' if buckets.empty?
291
+
292
+ @name = name.to_s.freeze
293
+ @labels = labels.freeze
294
+ @bucket_boundaries = buckets.sort.freeze
295
+ @created_at = Time.current.freeze
296
+
297
+ # Thread-safe counters for each bucket + infinity bucket
298
+ @bucket_counts = (@bucket_boundaries + [Float::INFINITY]).map do |_|
299
+ Concurrent::AtomicFixnum.new(0)
300
+ end
301
+
302
+ @count = Concurrent::AtomicFixnum.new(0)
303
+ @sum = Concurrent::AtomicReference.new(0.0)
304
+ end
305
+
306
+ # Observe a value and update histogram buckets
307
+ #
308
+ # @param value [Numeric] The observed value
309
+ # @return [Numeric] The observed value (for chaining)
310
+ # @raise [ArgumentError] If value is not numeric
311
+ def observe(value)
312
+ raise ArgumentError, "Observed value must be numeric, got: #{value.class}" unless value.is_a?(Numeric)
313
+
314
+ # Update count and sum atomically
315
+ @count.increment
316
+ @sum.update { |current| current + value }
317
+
318
+ # Increment all buckets where value <= boundary (cumulative histogram)
319
+ @bucket_boundaries.each_with_index do |boundary, index|
320
+ @bucket_counts[index].increment if value <= boundary
321
+ end
322
+
323
+ # Always increment the infinity bucket (total count)
324
+ @bucket_counts.last.increment
325
+
326
+ value
327
+ end
328
+
329
+ # Get the total number of observations
330
+ #
331
+ # @return [Integer] Total observation count
332
+ def count
333
+ @count.value
334
+ end
335
+
336
+ # Get the sum of all observed values
337
+ #
338
+ # @return [Numeric] Sum of observations
339
+ def sum
340
+ @sum.get
341
+ end
342
+
343
+ # Get the current bucket counts
344
+ #
345
+ # @return [Hash] Bucket boundaries to counts mapping
346
+ def buckets
347
+ result = {}
348
+ @bucket_boundaries.each_with_index do |boundary, index|
349
+ result[boundary] = @bucket_counts[index].value
350
+ end
351
+ result[Float::INFINITY] = @bucket_counts.last.value
352
+ result
353
+ end
354
+
355
+ # Calculate the average of observed values
356
+ #
357
+ # @return [Float] Average value, or 0.0 if no observations
358
+ def average
359
+ current_count = count
360
+ return 0.0 if current_count.zero?
361
+
362
+ sum.to_f / current_count
363
+ end
364
+
365
+ # Get a hash representation of this metric
366
+ #
367
+ # @return [Hash] Metric data including name, labels, buckets, count, sum, type
368
+ def to_h
369
+ {
370
+ name: name,
371
+ labels: labels,
372
+ buckets: buckets,
373
+ count: count,
374
+ sum: sum,
375
+ average: average,
376
+ type: :histogram,
377
+ created_at: created_at
378
+ }
379
+ end
380
+
381
+ # Reset the histogram (primarily for testing)
382
+ #
383
+ # @return [void]
384
+ def reset!
385
+ @count.value = 0
386
+ @sum.set(0.0)
387
+ @bucket_counts.each { |bucket| bucket.value = 0 }
388
+ end
389
+
390
+ # Get a description of this metric for debugging
391
+ #
392
+ # @return [String] Human-readable description
393
+ def description
394
+ label_str = labels.empty? ? '' : format_labels_for_description(labels)
395
+ "#{name}#{label_str} = #{count} observations, avg: #{average.round(3)} (histogram)"
396
+ end
397
+
398
+ private
399
+
400
+ # Format labels consistently across Ruby versions
401
+ # Uses the older Ruby hash syntax for backward compatibility with tests
402
+ #
403
+ # @param labels_hash [Hash] The labels hash to format
404
+ # @return [String] Formatted label string
405
+ def format_labels_for_description(labels_hash)
406
+ return '' if labels_hash.empty?
407
+
408
+ formatted_pairs = labels_hash.map do |key, value|
409
+ ":#{key}=>#{value.inspect}"
410
+ end
411
+ "{#{formatted_pairs.join(', ')}}"
412
+ end
413
+ end
414
+ end
415
+ end
416
+ end