commit 537c54df145fa0ee713746214ba8f69753616b08 Author: Marsway Date: Mon Jan 5 14:58:50 2026 +0800 init diff --git a/.env b/.env new file mode 100644 index 0000000..2903281 --- /dev/null +++ b/.env @@ -0,0 +1,9 @@ +APP_NAME=ConnectHub +DATA_DIR=/data +DB_URL=sqlite:////data/connecthub.db +REDIS_URL=redis://redis:6379/0 +FERNET_KEY_PATH=/data/fernet.key +DEV_MODE=1 +LOG_DIR=/data/logs + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..599c7cc --- /dev/null +++ b/README.md @@ -0,0 +1,142 @@ +## ConnectHub 开发手册 + +ConnectHub 是一个轻量级企业集成中间件:统一管理多系统集成任务(Job),提供定时调度、执行监控与“一键重试”。 + +### 项目结构树 + +``` +. +├── app +│ ├── admin +│ │ ├── routes.py +│ │ ├── templates +│ │ │ └── joblog_details.html +│ │ └── views.py +│ ├── core +│ │ ├── config.py +│ │ └── logging.py +│ ├── db +│ │ ├── crud.py +│ │ ├── engine.py +│ │ └── models.py +│ ├── integrations +│ │ └── base.py +│ ├── jobs +│ │ └── base.py +│ ├── plugins +│ │ └── manager.py +│ ├── security +│ │ └── fernet.py +│ ├── tasks +│ │ ├── celery_app.py +│ │ ├── dispatcher.py +│ │ └── execute.py +│ └── main.py +├── extensions +│ └── example +│ ├── client.py +│ └── job.py +├── docker +│ └── Dockerfile +├── docker-compose.yml +├── env.example +└── pyproject.toml +``` + +### 环境与配置 + +- `env.example`:环境变量示例(由于环境限制,仓库中使用该文件名;本地运行时请手动创建 `.env` 并参考此文件) +- 关键变量: + - `DATA_DIR=/data`:容器内数据目录 + - `DB_URL=sqlite:////data/connecthub.db`:SQLite DB 文件 + - `REDIS_URL=redis://redis:6379/0`:Celery Broker/Backend + - `FERNET_KEY_PATH=/data/fernet.key`:Fernet key 文件(自动生成并持久化) + - `LOG_DIR=/data/logs`:日志目录(可选) + +### 核心框架实现要点 + +#### BaseJob(插件规范) + +- 位置:`app/jobs/base.py` +- 规范:插件必须实现 `run(params, secrets)`,其中: + - `params` 来自 `Job.public_cfg`(明文) + - `secrets` 来自 `Job.secret_cfg` 解密后的明文(仅内存) + +#### BaseClient(适配器/SDK) + +- 位置:`app/integrations/base.py` +- 规范:业务 Job 禁止直接写 HTTP;必须通过 Client 访问外部系统(统一超时、重试、日志)。 + +#### Security(Fernet 加解密) + +- 位置:`app/security/fernet.py` +- 说明: + - `secret_cfg` 在数据库中保存 **Fernet 密文 token** + - Worker 执行前自动解密,仅在内存中传给 Job + - key 自动生成到 `FERNET_KEY_PATH`(默认 `/data/fernet.key`),volume 挂载后可持久化 + +#### PluginManager(动态加载) + +- 位置:`app/plugins/manager.py` +- `handler_path` 推荐格式:`extensions.example.job:ExampleJob` + +### 数据层与 Admin(SQLAdmin) + +- 模型:`app/db/models.py`(Job / JobLog) +- Admin: + - `Job`:可视化增删改查 + - `JobLog`:可视化查看执行日志(只读) + - `JobLog` 详情页自定义 `Retry` 按钮:点击后读取 `snapshot_params` 并触发重试 +- 关键文件: + - `app/admin/views.py`:ModelView 定义;保存 Job 时自动加密 `secret_cfg` + - `app/admin/templates/joblog_details.html`:JobLog 详情模板覆盖,加入 Retry 按钮 + - `app/admin/routes.py`:处理 Retry POST 并触发 Celery + +### 任务引擎(Celery) + +- Celery app:`app/tasks/celery_app.py` +- 调度: + - Beat 每分钟触发一次 `connecthub.dispatcher.tick` + - `dispatcher.tick` 读取 DB Jobs,根据 `cron_expr` 到点触发 `connecthub.execute_job` +- 执行: + - `app/tasks/execute.py` 的 `execute_job`:读库/解密/加载插件/执行/写 JobLog(含异常堆栈) + +### 运行指南 + +1. 在仓库根目录创建 `.env`(参考 `env.example`) +2. 生产模式启动: + +```bash +docker compose up -d --build +``` + +3. 打开 Admin: + - `http://localhost:8000/admin` +4. 创建一个示例 Job(ExampleJob): + - `id`: `example.hello` + - `cron_expr`: `* * * * *`(每分钟) + - `handler_path`: `extensions.example.job:ExampleJob` + - `public_cfg`: `{"name":"Mars"}` + - `secret_cfg`: `{"token":"demo-token"}`(保存时自动加密落库) +5. 等待 Beat 触发执行,或在 JobLog 里查看结果;若失败/想复跑,在 JobLog 详情页点击 **Retry**。 + +### 开发模式(实时更新代码) + +开发阶段可以使用 dev compose 叠加文件,实现: +- `backend`:`uvicorn --reload` +- `worker/beat`:监听代码变更后自动重启进程加载新代码 + +启动命令(二选一): + +```bash +# 方式 A:直接 docker compose 叠加 +docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build + +# 方式 B:使用脚本 +./connecthub.sh dev-build +./connecthub.sh dev-start +``` + +生产环境请只使用 `docker-compose.yml`(不挂载源码、不启用 reload/watch),发布通过重新 build 镜像完成。 + + diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..8fa2136 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,2 @@ +# + diff --git a/app/__pycache__/__init__.cpython-312.pyc b/app/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a63b814 Binary files /dev/null and b/app/__pycache__/__init__.cpython-312.pyc differ diff --git a/app/__pycache__/main.cpython-312.pyc b/app/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000..d836266 Binary files /dev/null and b/app/__pycache__/main.cpython-312.pyc differ diff --git a/app/admin/__init__.py b/app/admin/__init__.py new file mode 100644 index 0000000..2c2a87c --- /dev/null +++ b/app/admin/__init__.py @@ -0,0 +1,3 @@ +# + + diff --git a/app/admin/__pycache__/__init__.cpython-312.pyc b/app/admin/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ea6a4e0 Binary files /dev/null and b/app/admin/__pycache__/__init__.cpython-312.pyc differ diff --git a/app/admin/__pycache__/routes.cpython-312.pyc b/app/admin/__pycache__/routes.cpython-312.pyc new file mode 100644 index 0000000..123d3dc Binary files /dev/null and b/app/admin/__pycache__/routes.cpython-312.pyc differ diff --git a/app/admin/__pycache__/views.cpython-312.pyc b/app/admin/__pycache__/views.cpython-312.pyc new file mode 100644 index 0000000..a3b2fb0 Binary files /dev/null and b/app/admin/__pycache__/views.cpython-312.pyc differ diff --git a/app/admin/routes.py b/app/admin/routes.py new file mode 100644 index 0000000..27a3ff2 --- /dev/null +++ b/app/admin/routes.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from fastapi import APIRouter, HTTPException, Request +from starlette.responses import RedirectResponse + +from app.db import crud +from app.db.engine import get_session +from app.tasks.execute import execute_job + + +router = APIRouter() + + +@router.post("/admin/joblogs/{log_id}/retry") +def retry_joblog(request: Request, log_id: int): + session = get_session() + try: + log = crud.get_job_log(session, log_id) + if not log: + raise HTTPException(status_code=404, detail="JobLog not found") + # 关键:用 snapshot_params 重新触发任务(其中 secret_cfg 仍为密文) + execute_job.delay(snapshot_params=log.snapshot_params) + finally: + session.close() + + referer = request.headers.get("Referer") or "/admin" + return RedirectResponse(referer, status_code=303) + + +@router.post("/admin/jobs/{job_id}/run") +def run_job(request: Request, job_id: str): + # 触发一次立即执行 + execute_job.delay(job_id=job_id) + referer = request.headers.get("Referer") or "/admin" + return RedirectResponse(referer, status_code=303) + + diff --git a/app/admin/templates/job_details.html b/app/admin/templates/job_details.html new file mode 100644 index 0000000..d28a209 --- /dev/null +++ b/app/admin/templates/job_details.html @@ -0,0 +1,17 @@ +{% extends "sqladmin/details.html" %} + +{% block content %} + {{ super() }} + + +{% endblock %} + + diff --git a/app/admin/templates/job_list.html b/app/admin/templates/job_list.html new file mode 100644 index 0000000..7152902 --- /dev/null +++ b/app/admin/templates/job_list.html @@ -0,0 +1,309 @@ +{% extends "sqladmin/list.html" %} + +{% block content %} + + +
+
+
+
+
+
+
+

{{ model_view.name_plural }}

+
+ {% if model_view.can_export %} + {% if model_view.export_types | length > 1 %} + + {% elif model_view.export_types | length == 1 %} + + {% endif %} + {% endif %} + {% if model_view.can_create %} + + {% endif %} +
+
+
+
+ + {% if model_view.column_searchable_list %} +
+
+ + + +
+
+ {% endif %} +
+
+
+ + + + + + {% for name in model_view._list_prop_names %} + {% set label = model_view._column_labels.get(name, name) %} + + {% endfor %} + + + + + {% for row in pagination.rows %} + + + + {% for name in model_view._list_prop_names %} + {% set value, formatted_value = model_view.get_list_value(row, name) %} + {% if name in model_view._relation_names %} + {% if is_list( value ) %} + + {% else %} + + {% endif %} + {% else %} + + {% endif %} + {% endfor %} + + + {% endfor %} + +
+ {% if name in model_view._sort_fields %} + {% if request.query_params.get("sortBy") == name and request.query_params.get("sort") == "asc" %} + {{ + label }} + {% elif request.query_params.get("sortBy") == name and request.query_params.get("sort") == "desc" %} + {{ label + }} + {% else %} + {{ label }} + {% endif %} + {% else %} + {{ label }} + {% endif %} + Run Now
+ + + + {% if model_view.can_view_details %} + + + + {% endif %} + {% if model_view.can_edit %} + + + + {% endif %} + {% if model_view.can_delete %} + + + + {% endif %} + + {% for elem, formatted_elem in zip(value, formatted_value) %} + {% if model_view.show_compact_lists %} + ({{ formatted_elem }}) + {% else %} + {{ formatted_elem }}
+ {% endif %} + {% endfor %} +
{{ formatted_value }}{{ formatted_value }} +
+ +
+
+
+ +
+
+ {% if model_view.get_filters() %} +
+
+
+

Filters

+
+
+ {% for filter in model_view.get_filters() %} + {% if filter.has_operator %} +
+
{{ filter.title }}
+
+ {% set current_filter = request.query_params.get(filter.parameter_name, '') %} + {% set current_op = request.query_params.get(filter.parameter_name + '_op', '') %} + {% if current_filter %} +
+ Current: {{ current_op }} {{ current_filter }} + [Clear] +
+ {% endif %} +
+ {% for key, value in request.query_params.items() %} + {% if key != filter.parameter_name and key != filter.parameter_name + '_op' %} + + {% endif %} + {% endfor %} + + + +
+
+
+ {% else %} +
+
{{ filter.title }}
+
+ {% for lookup in filter.lookups(request, model_view.model, model_view._run_arbitrary_query) %} + + {{ lookup[1] }} + + {% endfor %} +
+
+ {% endif %} + {% endfor %} +
+
+
+ {% endif %} +
+
+
+ {% if model_view.can_delete %} + {% include 'sqladmin/modals/delete.html' %} + {% endif %} + + {% for custom_action in model_view._custom_actions_in_list %} + {% if custom_action in model_view._custom_actions_confirmation %} + {% with confirmation_message = model_view._custom_actions_confirmation[custom_action], custom_action=custom_action, + url=model_view._url_for_action(request, custom_action) %} + {% include 'sqladmin/modals/list_action_confirmation.html' %} + {% endwith %} + {% endif %} + {% endfor %} +
+{% endblock %} + + diff --git a/app/admin/templates/joblog_details.html b/app/admin/templates/joblog_details.html new file mode 100644 index 0000000..82340de --- /dev/null +++ b/app/admin/templates/joblog_details.html @@ -0,0 +1,119 @@ +{% extends "sqladmin/layout.html" %} +{% block content %} + + +
+
+
+

+ {% for pk in model_view.pk_columns -%} + {{ pk.name }} + {%- if not loop.last %};{% endif -%} + {% endfor %}: {{ get_object_identifier(model) }} +

+
+
+
+ + + + + + + + + {% for name in model_view._details_prop_names %} + {% set label = model_view._column_labels.get(name, name) %} + + + {% set value, formatted_value = model_view.get_detail_value(model, name) %} + {% if name in model_view._relation_names %} + {% if is_list( value ) %} + + {% else %} + + {% endif %} + {% else %} + + {% endif %} + + {% endfor %} + +
ColumnValue
{{ label }} + {% for elem, formatted_elem in zip(value, formatted_value) %} + {% if model_view.show_compact_lists %} + ({{ formatted_elem }}) + {% else %} + {{ formatted_elem }}
+ {% endif %} + {% endfor %} +
{{ formatted_value }}{{ formatted_value }}
+
+ +
+
+
+{% if model_view.can_delete %} +{% include 'sqladmin/modals/delete.html' %} +{% endif %} + +{% for custom_action in model_view._custom_actions_in_detail %} +{% if custom_action in model_view._custom_actions_confirmation %} +{% with confirmation_message = model_view._custom_actions_confirmation[custom_action], custom_action=custom_action, +url=model_view._url_for_action(request, custom_action) + '?pks=' + (get_object_identifier(model) | string) %} +{% include 'sqladmin/modals/details_action_confirmation.html' %} +{% endwith %} +{% endif %} +{% endfor %} +{% endblock %} + + diff --git a/app/admin/templates/joblog_list.html b/app/admin/templates/joblog_list.html new file mode 100644 index 0000000..cc87927 --- /dev/null +++ b/app/admin/templates/joblog_list.html @@ -0,0 +1,309 @@ +{% extends "sqladmin/list.html" %} + +{% block content %} + + +
+
+
+
+
+
+
+

{{ model_view.name_plural }}

+
+ {% if model_view.can_export %} + {% if model_view.export_types | length > 1 %} + + {% elif model_view.export_types | length == 1 %} + + {% endif %} + {% endif %} + {% if model_view.can_create %} + + {% endif %} +
+
+
+
+ + {% if model_view.column_searchable_list %} +
+
+ + + +
+
+ {% endif %} +
+
+
+ + + + + + {% for name in model_view._list_prop_names %} + {% set label = model_view._column_labels.get(name, name) %} + + {% endfor %} + + + + + {% for row in pagination.rows %} + + + + {% for name in model_view._list_prop_names %} + {% set value, formatted_value = model_view.get_list_value(row, name) %} + {% if name in model_view._relation_names %} + {% if is_list( value ) %} + + {% else %} + + {% endif %} + {% else %} + + {% endif %} + {% endfor %} + + + {% endfor %} + +
+ {% if name in model_view._sort_fields %} + {% if request.query_params.get("sortBy") == name and request.query_params.get("sort") == "asc" %} + {{ + label }} + {% elif request.query_params.get("sortBy") == name and request.query_params.get("sort") == "desc" %} + {{ label + }} + {% else %} + {{ label }} + {% endif %} + {% else %} + {{ label }} + {% endif %} + Retry
+ + + + {% if model_view.can_view_details %} + + + + {% endif %} + {% if model_view.can_edit %} + + + + {% endif %} + {% if model_view.can_delete %} + + + + {% endif %} + + {% for elem, formatted_elem in zip(value, formatted_value) %} + {% if model_view.show_compact_lists %} + ({{ formatted_elem }}) + {% else %} + {{ formatted_elem }}
+ {% endif %} + {% endfor %} +
{{ formatted_value }}{{ formatted_value }} +
+ +
+
+
+ +
+
+ {% if model_view.get_filters() %} +
+
+
+

Filters

+
+
+ {% for filter in model_view.get_filters() %} + {% if filter.has_operator %} +
+
{{ filter.title }}
+
+ {% set current_filter = request.query_params.get(filter.parameter_name, '') %} + {% set current_op = request.query_params.get(filter.parameter_name + '_op', '') %} + {% if current_filter %} +
+ Current: {{ current_op }} {{ current_filter }} + [Clear] +
+ {% endif %} +
+ {% for key, value in request.query_params.items() %} + {% if key != filter.parameter_name and key != filter.parameter_name + '_op' %} + + {% endif %} + {% endfor %} + + + +
+
+
+ {% else %} +
+
{{ filter.title }}
+
+ {% for lookup in filter.lookups(request, model_view.model, model_view._run_arbitrary_query) %} + + {{ lookup[1] }} + + {% endfor %} +
+
+ {% endif %} + {% endfor %} +
+
+
+ {% endif %} +
+
+
+ {% if model_view.can_delete %} + {% include 'sqladmin/modals/delete.html' %} + {% endif %} + + {% for custom_action in model_view._custom_actions_in_list %} + {% if custom_action in model_view._custom_actions_confirmation %} + {% with confirmation_message = model_view._custom_actions_confirmation[custom_action], custom_action=custom_action, + url=model_view._url_for_action(request, custom_action) %} + {% include 'sqladmin/modals/list_action_confirmation.html' %} + {% endwith %} + {% endif %} + {% endfor %} +
+{% endblock %} + + diff --git a/app/admin/views.py b/app/admin/views.py new file mode 100644 index 0000000..872278d --- /dev/null +++ b/app/admin/views.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +import json +from datetime import datetime +from typing import Any +from zoneinfo import ZoneInfo + +from croniter import croniter +from markupsafe import Markup +from sqladmin import ModelView, action +from sqladmin.models import Request +from starlette.responses import RedirectResponse + +from app.db.models import Job, JobLog +from app.plugins.manager import load_job_class +from app.security.fernet import encrypt_json +from app.tasks.execute import execute_job + + +def _maybe_json(value: Any) -> Any: + if isinstance(value, str): + s = value.strip() + if not s: + return value + try: + return json.loads(s) + except json.JSONDecodeError: + return value + return value + + +def _fmt_dt_seconds(dt: datetime | None) -> str: + if not dt: + return "" + # DB 中保存的时间多为 naive;按 UTC 解释后转换为 Asia/Shanghai 展示 + tz = ZoneInfo("Asia/Shanghai") + if dt.tzinfo is None: + dt = dt.replace(tzinfo=ZoneInfo("UTC")) + return dt.astimezone(tz).strftime("%Y-%m-%d %H:%M:%S") + + +def _truncate(s: str, n: int = 120) -> str: + s = s or "" + return (s[: n - 3] + "...") if len(s) > n else s + + +class JobAdmin(ModelView, model=Job): + name = "Job" + name_plural = "Jobs" + icon = "fa fa-cogs" + + column_list = [Job.id, Job.enabled, Job.cron_expr, Job.handler_path, Job.updated_at] + column_details_list = [ + Job.id, + Job.enabled, + Job.cron_expr, + Job.handler_path, + Job.public_cfg, + Job.secret_cfg, + Job.last_run_at, + Job.created_at, + Job.updated_at, + ] + + # 允许在表单中编辑主键(创建 Job 必填) + form_include_pk = True + form_columns = [Job.id, Job.enabled, Job.cron_expr, Job.handler_path, Job.public_cfg, Job.secret_cfg] + + # 为 Job 详情页指定模板(用于调整按钮间距) + details_template = "job_details.html" + + # 列表页模板:加入每行 Run Now + list_template = "job_list.html" + + @action( + name="run_now", + label="Run Now", + confirmation_message="Trigger this job now?", + add_in_list=True, + add_in_detail=True, + ) + async def run_now(self, request: Request): # type: ignore[override] + pks = request.query_params.get("pks", "").split(",") + for pk in [p for p in pks if p]: + execute_job.delay(job_id=pk) + referer = request.headers.get("Referer") + return RedirectResponse(referer or request.url_for("admin:list", identity=self.identity), status_code=303) + + async def on_model_change(self, data: dict, model: Job, is_created: bool, request) -> None: # type: ignore[override] + # id 必填(避免插入时触发 NOT NULL) + raw_id = data.get("id") if is_created else (data.get("id") or getattr(model, "id", None)) + if raw_id is None or not str(raw_id).strip(): + raise ValueError("id is required") + + # handler_path 强校验:必须可 import 且继承 BaseJob + handler_path = data.get("handler_path") if is_created else (data.get("handler_path") or model.handler_path) + if handler_path is None or not str(handler_path).strip(): + raise ValueError("handler_path is required") + load_job_class(str(handler_path).strip()) + + # cron_expr 校验:必须是合法 cron 表达式 + cron_expr = data.get("cron_expr") if is_created else (data.get("cron_expr") or model.cron_expr) + if cron_expr is None or not str(cron_expr).strip(): + raise ValueError("cron_expr is required") + base = datetime.now(ZoneInfo("Asia/Shanghai")) + itr = croniter(str(cron_expr).strip(), base) + _ = itr.get_next(datetime) + + # public_cfg 允许以 JSON 字符串输入 + pcfg = _maybe_json(data.get("public_cfg")) + if isinstance(pcfg, str): + raise ValueError("public_cfg must be a JSON object") + if isinstance(pcfg, dict): + data["public_cfg"] = pcfg + + # secret_cfg:若用户输入 JSON 字符串,则自动加密落库;若输入已是 token,则原样保存 + scfg = data.get("secret_cfg", "") + if scfg is None: + data["secret_cfg"] = "" + return + if isinstance(scfg, str): + s = scfg.strip() + if not s: + data["secret_cfg"] = "" + return + parsed = _maybe_json(s) + if isinstance(parsed, dict): + data["secret_cfg"] = encrypt_json(parsed) + else: + # 非 JSON:视为已加密 token + data["secret_cfg"] = s + return + if isinstance(scfg, dict): + data["secret_cfg"] = encrypt_json(scfg) + return + raise ValueError("secret_cfg must be JSON object or encrypted token string") + + +class JobLogAdmin(ModelView, model=JobLog): + name = "JobLog" + name_plural = "JobLogs" + icon = "fa fa-list" + + can_create = False + can_edit = False + can_delete = False + + # 列表更适合扫读:保留关键字段 + message(截断) + column_list = [JobLog.id, JobLog.job_id, JobLog.status, JobLog.started_at, JobLog.finished_at, JobLog.message] + # 默认按 started_at 倒序(最新在前) + column_default_sort = [(JobLog.started_at, True)] + column_details_list = [ + JobLog.id, + JobLog.job_id, + JobLog.status, + JobLog.snapshot_params, + JobLog.message, + JobLog.traceback, + JobLog.run_log, + JobLog.celery_task_id, + JobLog.attempt, + JobLog.started_at, + JobLog.finished_at, + ] + + # 列表页模板:加入每行 Retry + list_template = "joblog_list.html" + # 为 JobLog 详情页单独指定模板(用于加入 Retry 按钮) + details_template = "joblog_details.html" + + column_formatters = { + JobLog.started_at: lambda m, a: _fmt_dt_seconds(m.started_at), + JobLog.finished_at: lambda m, a: _fmt_dt_seconds(m.finished_at), + JobLog.message: lambda m, a: _truncate(m.message, 120), + } + + column_formatters_detail = { + JobLog.started_at: lambda m, a: _fmt_dt_seconds(m.started_at), + JobLog.finished_at: lambda m, a: _fmt_dt_seconds(m.finished_at), + JobLog.traceback: lambda m, a: Markup(f"
{m.traceback or ''}
"), + JobLog.run_log: lambda m, a: Markup( + "
"
+            + (m.run_log or "")
+            + "
" + ), + JobLog.snapshot_params: lambda m, a: Markup( + "
"
+            + json.dumps(m.snapshot_params or {}, ensure_ascii=False, indent=2, sort_keys=True)
+            + "
" + ), + } diff --git a/app/core/__init__.py b/app/core/__init__.py new file mode 100644 index 0000000..2c2a87c --- /dev/null +++ b/app/core/__init__.py @@ -0,0 +1,3 @@ +# + + diff --git a/app/core/__pycache__/__init__.cpython-312.pyc b/app/core/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f6ef2d3 Binary files /dev/null and b/app/core/__pycache__/__init__.cpython-312.pyc differ diff --git a/app/core/__pycache__/config.cpython-312.pyc b/app/core/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..752091d Binary files /dev/null and b/app/core/__pycache__/config.cpython-312.pyc differ diff --git a/app/core/__pycache__/log_capture.cpython-312.pyc b/app/core/__pycache__/log_capture.cpython-312.pyc new file mode 100644 index 0000000..1f5f7c9 Binary files /dev/null and b/app/core/__pycache__/log_capture.cpython-312.pyc differ diff --git a/app/core/__pycache__/logging.cpython-312.pyc b/app/core/__pycache__/logging.cpython-312.pyc new file mode 100644 index 0000000..edff46a Binary files /dev/null and b/app/core/__pycache__/logging.cpython-312.pyc differ diff --git a/app/core/config.py b/app/core/config.py new file mode 100644 index 0000000..e29b97d --- /dev/null +++ b/app/core/config.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file=".env", extra="ignore") + + app_name: str = "ConnectHub" + data_dir: str = "/data" + db_url: str = "sqlite:////data/connecthub.db" + redis_url: str = "redis://redis:6379/0" + fernet_key_path: str = "/data/fernet.key" + dev_mode: bool = False + log_dir: str | None = "/data/logs" + + +settings = Settings() + + diff --git a/app/core/log_capture.py b/app/core/log_capture.py new file mode 100644 index 0000000..46dd4b6 --- /dev/null +++ b/app/core/log_capture.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +import logging +from contextlib import contextmanager +from typing import Callable, Iterator + + +class SafeBufferingHandler(logging.Handler): + """ + 只用于“尽力捕获”运行日志: + - emit 内部全 try/except,任何异常都吞掉,绝不影响任务执行 + - 有最大字节限制,超过后写入截断标记并停止追加 + """ + + def __init__(self, *, max_bytes: int = 200_000, level: int = logging.INFO) -> None: + super().__init__(level=level) + self.max_bytes = max_bytes + self._buf: list[str] = [] + self._size_bytes = 0 + self._truncated = False + + def emit(self, record: logging.LogRecord) -> None: # noqa: D401 + try: + if self._truncated: + return + try: + msg = self.format(record) + except Exception: + return + line = msg + "\n" + try: + b = line.encode("utf-8", errors="replace") + except Exception: + return + + if self._size_bytes + len(b) > self.max_bytes: + self._buf.append("[TRUNCATED] run_log exceeded max_bytes\n") + self._truncated = True + return + + self._buf.append(line) + self._size_bytes += len(b) + except Exception: + # 双保险:任何异常都不能冒泡 + return + + def get_text(self) -> str: + try: + return "".join(self._buf) + except Exception: + return "" + + +@contextmanager +def capture_logs(*, max_bytes: int = 200_000) -> Iterator[Callable[[], str]]: + """ + 捕获当前进程(root logger)输出的日志文本。 + 任何问题都不应影响业务执行。 + """ + root = logging.getLogger() + handler = SafeBufferingHandler(max_bytes=max_bytes) + handler.setLevel(logging.INFO) + handler.setFormatter( + logging.Formatter(fmt="%(asctime)s %(levelname)s %(name)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") + ) + + try: + root.addHandler(handler) + except Exception: + # 无法挂载则降级为空 + yield lambda: "" + return + + try: + yield handler.get_text + finally: + try: + root.removeHandler(handler) + except Exception: + pass + + diff --git a/app/core/logging.py b/app/core/logging.py new file mode 100644 index 0000000..1fa274a --- /dev/null +++ b/app/core/logging.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import logging +import os +from logging.handlers import RotatingFileHandler + +from app.core.config import settings + + +def setup_logging() -> None: + logger = logging.getLogger() + if getattr(logger, "_connecthub_configured", False): + return + + logger.setLevel(logging.INFO) + formatter = logging.Formatter( + fmt="%(asctime)s %(levelname)s %(name)s %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + sh = logging.StreamHandler() + sh.setFormatter(formatter) + logger.addHandler(sh) + + if settings.log_dir: + os.makedirs(settings.log_dir, exist_ok=True) + fh = RotatingFileHandler( + os.path.join(settings.log_dir, "connecthub.log"), + maxBytes=10 * 1024 * 1024, + backupCount=5, + ) + fh.setFormatter(formatter) + logger.addHandler(fh) + + setattr(logger, "_connecthub_configured", True) + + diff --git a/app/db/__init__.py b/app/db/__init__.py new file mode 100644 index 0000000..2c2a87c --- /dev/null +++ b/app/db/__init__.py @@ -0,0 +1,3 @@ +# + + diff --git a/app/db/__pycache__/__init__.cpython-312.pyc b/app/db/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..47581d3 Binary files /dev/null and b/app/db/__pycache__/__init__.cpython-312.pyc differ diff --git a/app/db/__pycache__/crud.cpython-312.pyc b/app/db/__pycache__/crud.cpython-312.pyc new file mode 100644 index 0000000..f03ecdc Binary files /dev/null and b/app/db/__pycache__/crud.cpython-312.pyc differ diff --git a/app/db/__pycache__/engine.cpython-312.pyc b/app/db/__pycache__/engine.cpython-312.pyc new file mode 100644 index 0000000..07a82f4 Binary files /dev/null and b/app/db/__pycache__/engine.cpython-312.pyc differ diff --git a/app/db/__pycache__/models.cpython-312.pyc b/app/db/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000..bcc049f Binary files /dev/null and b/app/db/__pycache__/models.cpython-312.pyc differ diff --git a/app/db/__pycache__/schema.cpython-312.pyc b/app/db/__pycache__/schema.cpython-312.pyc new file mode 100644 index 0000000..e0925e3 Binary files /dev/null and b/app/db/__pycache__/schema.cpython-312.pyc differ diff --git a/app/db/crud.py b/app/db/crud.py new file mode 100644 index 0000000..56c55ee --- /dev/null +++ b/app/db/crud.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from datetime import datetime +from typing import Any + +from sqlalchemy import select +from sqlalchemy.orm import Session + +from app.db.models import Job, JobLog, JobStatus + + +def get_job(session: Session, job_id: str) -> Job | None: + return session.get(Job, job_id) + + +def list_enabled_jobs(session: Session) -> list[Job]: + return list(session.scalars(select(Job).where(Job.enabled.is_(True)))) + + +def update_job_last_run_at(session: Session, job_id: str, dt: datetime) -> None: + job = session.get(Job, job_id) + if not job: + return + job.last_run_at = dt + session.add(job) + session.commit() + + +def create_job_log( + session: Session, + *, + job_id: str, + status: JobStatus, + snapshot_params: dict[str, Any], + message: str = "", + traceback: str = "", + run_log: str = "", + celery_task_id: str = "", + attempt: int = 0, + started_at: datetime | None = None, + finished_at: datetime | None = None, +) -> JobLog: + log = JobLog( + job_id=job_id, + status=status, + snapshot_params=snapshot_params, + message=message, + traceback=traceback, + run_log=run_log, + celery_task_id=celery_task_id, + attempt=attempt, + started_at=started_at or datetime.utcnow(), + finished_at=finished_at, + ) + session.add(log) + session.commit() + session.refresh(log) + return log + + +def get_job_log(session: Session, log_id: int) -> JobLog | None: + return session.get(JobLog, log_id) + + diff --git a/app/db/engine.py b/app/db/engine.py new file mode 100644 index 0000000..247e489 --- /dev/null +++ b/app/db/engine.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from sqlalchemy import create_engine +from sqlalchemy.orm import Session, sessionmaker + +from app.core.config import settings + + +engine = create_engine( + settings.db_url, + connect_args={"check_same_thread": False} if settings.db_url.startswith("sqlite") else {}, + future=True, +) + +SessionLocal = sessionmaker(bind=engine, class_=Session, autoflush=False, autocommit=False, future=True) + + +def get_session() -> Session: + return SessionLocal() + + diff --git a/app/db/models.py b/app/db/models.py new file mode 100644 index 0000000..b024dd3 --- /dev/null +++ b/app/db/models.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import enum +from datetime import datetime +from typing import Any + +from sqlalchemy import JSON, Boolean, DateTime, Enum, ForeignKey, Integer, String, Text, func +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship + + +class Base(DeclarativeBase): + pass + + +class Job(Base): + __tablename__ = "jobs" + + id: Mapped[str] = mapped_column(String, primary_key=True) + cron_expr: Mapped[str] = mapped_column(String, nullable=False) + handler_path: Mapped[str] = mapped_column(String, nullable=False) + public_cfg: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False) + # 密文 token(Fernet 加密后的字符串) + secret_cfg: Mapped[str] = mapped_column(Text, default="", nullable=False) + + enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) + last_run_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) + created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False + ) + + logs: Mapped[list["JobLog"]] = relationship(back_populates="job", cascade="all, delete-orphan") + + +class JobStatus(str, enum.Enum): + SUCCESS = "SUCCESS" + FAILURE = "FAILURE" + RETRY = "RETRY" + + +class JobLog(Base): + __tablename__ = "job_logs" + + id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) + job_id: Mapped[str] = mapped_column(ForeignKey("jobs.id"), index=True, nullable=False) + + status: Mapped[JobStatus] = mapped_column( + Enum(JobStatus, native_enum=False, length=16), + nullable=False, + ) + snapshot_params: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False) + + message: Mapped[str] = mapped_column(Text, default="", nullable=False) + traceback: Mapped[str] = mapped_column(Text, default="", nullable=False) + # 本次执行期间捕获到的完整运行日志(可能很长,按上层捕获器做截断) + run_log: Mapped[str] = mapped_column(Text, default="", nullable=False) + celery_task_id: Mapped[str] = mapped_column(String, default="", nullable=False) + attempt: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + + started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) + finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) + + job: Mapped[Job] = relationship(back_populates="logs") + + diff --git a/app/db/schema.py b/app/db/schema.py new file mode 100644 index 0000000..8ad738e --- /dev/null +++ b/app/db/schema.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from sqlalchemy import Engine, text + +from app.db.models import Base + + +def _has_column(conn, table: str, col: str) -> bool: + rows = conn.execute(text(f"PRAGMA table_info({table})")).fetchall() + return any(r[1] == col for r in rows) # PRAGMA columns: (cid, name, type, notnull, dflt_value, pk) + + +def ensure_schema(engine: Engine) -> None: + """ + SQLite 轻量自升级: + - create_all 不会更新既有表结构,因此用 PRAGMA + ALTER TABLE 补列 + - 必须保证任何失败都不影响主流程(上层可选择忽略异常) + """ + Base.metadata.create_all(bind=engine) + + with engine.begin() as conn: + # job_logs.run_log + if not _has_column(conn, "job_logs", "run_log"): + conn.execute(text("ALTER TABLE job_logs ADD COLUMN run_log TEXT NOT NULL DEFAULT ''")) + + diff --git a/app/integrations/__init__.py b/app/integrations/__init__.py new file mode 100644 index 0000000..62b5580 --- /dev/null +++ b/app/integrations/__init__.py @@ -0,0 +1,5 @@ +"""系统集成适配器""" + +from app.integrations.base import BaseClient + +__all__ = ["BaseClient"] \ No newline at end of file diff --git a/app/integrations/__pycache__/__init__.cpython-312.pyc b/app/integrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..17af24d Binary files /dev/null and b/app/integrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/app/integrations/__pycache__/base.cpython-312.pyc b/app/integrations/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..c1feeb4 Binary files /dev/null and b/app/integrations/__pycache__/base.cpython-312.pyc differ diff --git a/app/integrations/__pycache__/base_client.cpython-312.pyc b/app/integrations/__pycache__/base_client.cpython-312.pyc new file mode 100644 index 0000000..ef7708f Binary files /dev/null and b/app/integrations/__pycache__/base_client.cpython-312.pyc differ diff --git a/app/integrations/base.py b/app/integrations/base.py new file mode 100644 index 0000000..bee6795 --- /dev/null +++ b/app/integrations/base.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import logging +import time +from typing import Any + +import httpx + + +logger = logging.getLogger("connecthub.integrations") + + +class BaseClient: + """ + 统一的外部系统访问 SDK 基类。 + 业务 Job 禁止直接写 HTTP,只能调用 integrations 下的 Client。 + """ + + def __init__( + self, + *, + base_url: str, + timeout_s: float = 10.0, + retries: int = 2, + retry_backoff_s: float = 0.5, + headers: dict[str, str] | None = None, + ) -> None: + self.base_url = base_url.rstrip("/") + self.timeout_s = timeout_s + self.retries = retries + self.retry_backoff_s = retry_backoff_s + self.headers = headers or {} + + self._client = httpx.Client( + base_url=self.base_url, + timeout=httpx.Timeout(self.timeout_s), + headers=self.headers, + ) + + def close(self) -> None: + self._client.close() + + def request(self, method: str, path: str, **kwargs: Any) -> httpx.Response: + url = path if path.startswith("/") else f"/{path}" + last_exc: Exception | None = None + for attempt in range(self.retries + 1): + try: + start = time.time() + resp = self._client.request(method=method, url=url, **kwargs) + elapsed_ms = int((time.time() - start) * 1000) + logger.info("HTTP %s %s -> %s (%sms)", method, url, resp.status_code, elapsed_ms) + resp.raise_for_status() + return resp + except Exception as e: # noqa: BLE001 (framework-wide) + last_exc = e + logger.warning("HTTP failed (%s %s) attempt=%s err=%r", method, url, attempt + 1, e) + if attempt < self.retries: + time.sleep(self.retry_backoff_s * (2**attempt)) + continue + raise + assert last_exc is not None + raise last_exc + + def get_json(self, path: str, **kwargs: Any) -> Any: + return self.request("GET", path, **kwargs).json() + + def post_json(self, path: str, json: Any = None, **kwargs: Any) -> Any: + return self.request("POST", path, json=json, **kwargs).json() + + diff --git a/app/jobs/__init__.py b/app/jobs/__init__.py new file mode 100644 index 0000000..2c2a87c --- /dev/null +++ b/app/jobs/__init__.py @@ -0,0 +1,3 @@ +# + + diff --git a/app/jobs/__pycache__/__init__.cpython-312.pyc b/app/jobs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..15835a0 Binary files /dev/null and b/app/jobs/__pycache__/__init__.cpython-312.pyc differ diff --git a/app/jobs/__pycache__/base.cpython-312.pyc b/app/jobs/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..7fe7fa4 Binary files /dev/null and b/app/jobs/__pycache__/base.cpython-312.pyc differ diff --git a/app/jobs/base.py b/app/jobs/base.py new file mode 100644 index 0000000..e987975 --- /dev/null +++ b/app/jobs/base.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Any + + +class BaseJob(ABC): + """ + 插件 Job 基类:框架层只负责加载与调度。 + + - params: 来自 Job.public_cfg(明文 JSON) + - secrets: 来自 Job.secret_cfg 解密后的明文 dict(仅在内存中存在) + """ + + job_id: str | None = None + + @abstractmethod + def run(self, params: dict[str, Any], secrets: dict[str, Any]) -> dict[str, Any] | None: + raise NotImplementedError + + diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..922551a --- /dev/null +++ b/app/main.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import os + +from fastapi import FastAPI +from sqladmin import Admin + +from app.admin.routes import router as admin_router +from app.admin.views import JobAdmin, JobLogAdmin +from app.core.config import settings +from app.core.logging import setup_logging +from app.db.engine import engine +from app.db.schema import ensure_schema +from app.security.fernet import get_or_create_fernet_key + + +def _init_db() -> None: + ensure_schema(engine) + + +def _ensure_runtime() -> None: + # 确保 data 目录存在 + os.makedirs(settings.data_dir, exist_ok=True) + if settings.log_dir: + os.makedirs(settings.log_dir, exist_ok=True) + # 确保 Fernet key 准备好(或自动生成) + get_or_create_fernet_key(settings.fernet_key_path) + _init_db() + + +def create_app() -> FastAPI: + setup_logging() + _ensure_runtime() + + app = FastAPI(title=settings.app_name) + + app.include_router(admin_router) + + admin = Admin(app=app, engine=engine, templates_dir="app/admin/templates") + admin.add_view(JobAdmin) + admin.add_view(JobLogAdmin) + + @app.get("/health") + def health(): + return {"ok": True, "name": settings.app_name} + + return app + + +app = create_app() + + diff --git a/app/plugins/__init__.py b/app/plugins/__init__.py new file mode 100644 index 0000000..2c2a87c --- /dev/null +++ b/app/plugins/__init__.py @@ -0,0 +1,3 @@ +# + + diff --git a/app/plugins/__pycache__/__init__.cpython-312.pyc b/app/plugins/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..228883b Binary files /dev/null and b/app/plugins/__pycache__/__init__.cpython-312.pyc differ diff --git a/app/plugins/__pycache__/manager.cpython-312.pyc b/app/plugins/__pycache__/manager.cpython-312.pyc new file mode 100644 index 0000000..acad227 Binary files /dev/null and b/app/plugins/__pycache__/manager.cpython-312.pyc differ diff --git a/app/plugins/manager.py b/app/plugins/manager.py new file mode 100644 index 0000000..63f2e08 --- /dev/null +++ b/app/plugins/manager.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +import importlib +from dataclasses import dataclass +from typing import Type + +from app.jobs.base import BaseJob + + +@dataclass(frozen=True) +class HandlerRef: + module: str + cls_name: str + + +def parse_handler_path(handler_path: str) -> HandlerRef: + """ + 支持两种格式: + - "pkg.mod:ClassName"(推荐) + - "pkg.mod.ClassName" + """ + if ":" in handler_path: + module, cls_name = handler_path.split(":", 1) + return HandlerRef(module=module, cls_name=cls_name) + if "." not in handler_path: + raise ValueError(f"Invalid handler_path: {handler_path}") + module, cls_name = handler_path.rsplit(".", 1) + return HandlerRef(module=module, cls_name=cls_name) + + +def load_job_class(handler_path: str) -> Type[BaseJob]: + ref = parse_handler_path(handler_path) + mod = importlib.import_module(ref.module) + cls = getattr(mod, ref.cls_name, None) + if cls is None: + raise ImportError(f"Class not found: {ref.module}.{ref.cls_name}") + if not isinstance(cls, type) or not issubclass(cls, BaseJob): + raise TypeError(f"Handler is not a BaseJob subclass: {handler_path}") + return cls + + +def instantiate(handler_path: str) -> BaseJob: + cls = load_job_class(handler_path) + return cls() + + diff --git a/app/security/__init__.py b/app/security/__init__.py new file mode 100644 index 0000000..2c2a87c --- /dev/null +++ b/app/security/__init__.py @@ -0,0 +1,3 @@ +# + + diff --git a/app/security/__pycache__/__init__.cpython-312.pyc b/app/security/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7e26618 Binary files /dev/null and b/app/security/__pycache__/__init__.cpython-312.pyc differ diff --git a/app/security/__pycache__/fernet.cpython-312.pyc b/app/security/__pycache__/fernet.cpython-312.pyc new file mode 100644 index 0000000..261fe15 Binary files /dev/null and b/app/security/__pycache__/fernet.cpython-312.pyc differ diff --git a/app/security/fernet.py b/app/security/fernet.py new file mode 100644 index 0000000..dcd6e90 --- /dev/null +++ b/app/security/fernet.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +import json +import os +from typing import Any + +from cryptography.fernet import Fernet, InvalidToken + +from app.core.config import settings + + +def _ensure_parent_dir(path: str) -> None: + parent = os.path.dirname(path) + if parent: + os.makedirs(parent, exist_ok=True) + + +def get_or_create_fernet_key(path: str | None = None) -> bytes: + key_path = path or settings.fernet_key_path + _ensure_parent_dir(key_path) + + if os.path.exists(key_path): + with open(key_path, "rb") as f: + return f.read().strip() + + key = Fernet.generate_key() + # best-effort set 0o600 (not always supported on some FS) + try: + flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL + fd = os.open(key_path, flags, 0o600) + with os.fdopen(fd, "wb") as f: + f.write(key) + f.write(b"\n") + except FileExistsError: + # race: another process wrote it + with open(key_path, "rb") as f: + return f.read().strip() + except OSError: + with open(key_path, "wb") as f: + f.write(key) + f.write(b"\n") + return key + + +def _fernet() -> Fernet: + return Fernet(get_or_create_fernet_key()) + + +def encrypt_json(obj: dict[str, Any]) -> str: + data = json.dumps(obj, ensure_ascii=False, separators=(",", ":"), sort_keys=True).encode("utf-8") + return _fernet().encrypt(data).decode("utf-8") + + +def decrypt_json(token: str) -> dict[str, Any]: + if not token: + return {} + try: + raw = _fernet().decrypt(token.encode("utf-8")) + except InvalidToken as e: + raise ValueError("Invalid secret_cfg token (Fernet)") from e + return json.loads(raw.decode("utf-8")) + + diff --git a/app/tasks/__init__.py b/app/tasks/__init__.py new file mode 100644 index 0000000..2c2a87c --- /dev/null +++ b/app/tasks/__init__.py @@ -0,0 +1,3 @@ +# + + diff --git a/app/tasks/__pycache__/__init__.cpython-312.pyc b/app/tasks/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ce65129 Binary files /dev/null and b/app/tasks/__pycache__/__init__.cpython-312.pyc differ diff --git a/app/tasks/__pycache__/celery_app.cpython-312.pyc b/app/tasks/__pycache__/celery_app.cpython-312.pyc new file mode 100644 index 0000000..c11a306 Binary files /dev/null and b/app/tasks/__pycache__/celery_app.cpython-312.pyc differ diff --git a/app/tasks/__pycache__/dispatcher.cpython-312.pyc b/app/tasks/__pycache__/dispatcher.cpython-312.pyc new file mode 100644 index 0000000..f0552cf Binary files /dev/null and b/app/tasks/__pycache__/dispatcher.cpython-312.pyc differ diff --git a/app/tasks/__pycache__/execute.cpython-312.pyc b/app/tasks/__pycache__/execute.cpython-312.pyc new file mode 100644 index 0000000..4949e2e Binary files /dev/null and b/app/tasks/__pycache__/execute.cpython-312.pyc differ diff --git a/app/tasks/celery_app.py b/app/tasks/celery_app.py new file mode 100644 index 0000000..be7a578 --- /dev/null +++ b/app/tasks/celery_app.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from celery import Celery + +from app.core.config import settings + + +celery_app = Celery( + "connecthub", + broker=settings.redis_url, + backend=settings.redis_url, +) + +celery_app.conf.update( + task_serializer="json", + accept_content=["json"], + result_serializer="json", + enable_utc=False, + timezone="Asia/Shanghai", + # 明确包含 task 模块,避免 autodiscover 找不到(也避免导入导致循环依赖) + include=[ + "app.tasks.execute", + "app.tasks.dispatcher", + ], + beat_schedule={ + "connecthub-dispatcher-tick-every-minute": { + "task": "connecthub.dispatcher.tick", + "schedule": 60.0, + } + }, + worker_redirect_stdouts=False +) + + diff --git a/app/tasks/dispatcher.py b/app/tasks/dispatcher.py new file mode 100644 index 0000000..da8d6e0 --- /dev/null +++ b/app/tasks/dispatcher.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import logging +from datetime import datetime, timedelta +from zoneinfo import ZoneInfo + +from croniter import croniter + +from app.core.logging import setup_logging +from app.db import crud +from app.db.engine import get_session +from app.tasks.celery_app import celery_app +from app.tasks.execute import execute_job + + +logger = logging.getLogger("connecthub.tasks.dispatcher") + + +def _floor_to_minute(dt: datetime) -> datetime: + return dt.replace(second=0, microsecond=0) + + +@celery_app.task(name="connecthub.dispatcher.tick") +def tick() -> dict[str, int]: + """ + Beat 每分钟触发一次: + - 读取 enabled Jobs + - cron_expr 到点则触发 execute_job + - last_run_at 防止同一分钟重复触发 + """ + setup_logging() + + session = get_session() + tz = ZoneInfo("Asia/Shanghai") + now = datetime.now(tz) + now_min = _floor_to_minute(now) + triggered = 0 + + try: + for job in crud.list_enabled_jobs(session): + last = job.last_run_at + if last is not None: + # SQLite 通常存 naive datetime;按 Asia/Shanghai 解释 + if last.tzinfo is None: + last_min = _floor_to_minute(last.replace(tzinfo=tz)) + else: + last_min = _floor_to_minute(last.astimezone(tz)) + if last_min >= now_min: + continue + + # croniter 默认按传入 datetime 计算,这里用 Asia/Shanghai + base = now_min - timedelta(minutes=1) + itr = croniter(job.cron_expr, base) + nxt = itr.get_next(datetime) + if _floor_to_minute(nxt.replace(tzinfo=tz)) != now_min: + continue + + execute_job.delay(job_id=job.id) + crud.update_job_last_run_at(session, job.id, now_min.replace(tzinfo=None)) + triggered += 1 + + except Exception: # noqa: BLE001 + logger.exception("dispatcher.tick failed") + finally: + session.close() + + return {"triggered": triggered} + + + diff --git a/app/tasks/execute.py b/app/tasks/execute.py new file mode 100644 index 0000000..3517e78 --- /dev/null +++ b/app/tasks/execute.py @@ -0,0 +1,112 @@ +from __future__ import annotations + +import logging +import traceback as tb +from datetime import datetime +from typing import Any + +from app.core.log_capture import capture_logs +from app.core.logging import setup_logging +from app.db import crud +from app.db.engine import engine, get_session +from app.db.models import JobStatus +from app.db.schema import ensure_schema +from app.plugins.manager import instantiate +from app.security.fernet import decrypt_json +from app.tasks.celery_app import celery_app + + +logger = logging.getLogger("connecthub.tasks.execute") + + +@celery_app.task(bind=True, name="connecthub.execute_job") +def execute_job(self, job_id: str | None = None, snapshot_params: dict[str, Any] | None = None) -> dict[str, Any]: + """ + 通用执行入口: + - 传 job_id:从 DB 读取 Job 定义 + - 传 snapshot_params:按快照重跑(用于 Admin 一键重试) + """ + setup_logging() + + # 确保 schema 已升级(即使 worker 先启动也不会写库失败) + try: + ensure_schema(engine) + except Exception: + # schema upgrade 失败不能影响执行(最多导致 run_log 无法写入) + pass + + started_at = datetime.utcnow() + session = get_session() + status = JobStatus.SUCCESS + message = "" + traceback = "" + result: dict[str, Any] = {} + run_log_text = "" + + try: + with capture_logs(max_bytes=200_000) as get_run_log: + try: + if snapshot_params: + job_id = snapshot_params["job_id"] + handler_path = snapshot_params["handler_path"] + public_cfg = snapshot_params.get("public_cfg", {}) or {} + secret_token = snapshot_params.get("secret_cfg", "") or "" + else: + if not job_id: + raise ValueError("job_id or snapshot_params is required") + job = crud.get_job(session, job_id) + if not job: + raise ValueError(f"Job not found: {job_id}") + handler_path = job.handler_path + public_cfg = job.public_cfg or {} + secret_token = job.secret_cfg or "" + + secrets = decrypt_json(secret_token) + job_instance = instantiate(handler_path) + out = job_instance.run(params=public_cfg, secrets=secrets) + if isinstance(out, dict): + result = out + message = "OK" + + except Exception as e: # noqa: BLE001 (framework-wide) + # 如果是 Celery retry 触发,框架可在此处扩展为自动 retry;此版本先记录失败信息 + status = JobStatus.FAILURE + message = repr(e) + traceback = tb.format_exc() + logger.exception("execute_job failed job_id=%s", job_id) + finally: + try: + run_log_text = get_run_log() or "" + except Exception: + run_log_text = "" + finally: + finished_at = datetime.utcnow() + snapshot = snapshot_params or { + "job_id": job_id, + "handler_path": handler_path if "handler_path" in locals() else "", + "public_cfg": public_cfg if "public_cfg" in locals() else {}, + "secret_cfg": secret_token if "secret_token" in locals() else "", + "meta": { + "trigger": "celery", + "celery_task_id": getattr(self.request, "id", "") or "", + "started_at": started_at.isoformat(), + }, + } + crud.create_job_log( + session, + job_id=str(job_id or ""), + status=status, + snapshot_params=snapshot, + message=message, + traceback=traceback, + run_log=run_log_text, + celery_task_id=getattr(self.request, "id", "") or "", + attempt=int(getattr(self.request, "retries", 0) or 0), + started_at=started_at, + finished_at=finished_at, + ) + session.close() + + return {"status": status.value, "job_id": job_id, "result": result, "message": message} + + diff --git a/connecthub.sh b/connecthub.sh new file mode 100755 index 0000000..8923418 --- /dev/null +++ b/connecthub.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$ROOT_DIR" + +COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}" + +require_env() { + if [[ ! -f ".env" ]]; then + cat <<'EOF' +缺少 .env 文件。 +请在仓库根目录创建 .env(参考 env.example),例如: + cp env.example .env +然后按需修改其中变量。 +EOF + exit 1 + fi +} + +usage() { + cat <&2 + exit 2 + fi + tail="$2" + shift 2 + ;; + --tail=*) + tail="${1#*=}" + shift + ;; + -h|--help|help) + log_usage + exit 0 + ;; + *) + if [[ -z "$service" && "${1:0:1}" != "-" ]]; then + service="$1" + shift + else + echo "未知参数: $1" >&2 + echo + log_usage + exit 2 + fi + ;; + esac + done + + args=(docker compose -f "$COMPOSE_FILE" logs) + if [[ "$follow" = "1" ]]; then + args+=(--follow) + fi + if [[ -n "$tail" ]]; then + args+=(--tail "$tail") + fi + if [[ -n "$service" ]]; then + args+=("$service") + fi + "${args[@]}" + ;; + -h|--help|help|"") + usage + ;; + *) + echo "未知命令: $cmd" >&2 + echo + usage + exit 2 + ;; +esac + + diff --git a/data/connecthub.db b/data/connecthub.db new file mode 100644 index 0000000..96b09c1 Binary files /dev/null and b/data/connecthub.db differ diff --git a/data/fernet.key b/data/fernet.key new file mode 100644 index 0000000..d9eea3e --- /dev/null +++ b/data/fernet.key @@ -0,0 +1 @@ +hiwc97uAjCqHbteGdrN9BKaV9iHO4aV-_FfRJTn2Mo8= diff --git a/data/logs/connecthub.log b/data/logs/connecthub.log new file mode 100644 index 0000000..dc6d23c --- /dev/null +++ b/data/logs/connecthub.log @@ -0,0 +1,1450 @@ +2026-01-05 03:34:22 ERROR sqladmin.application (sqlite3.IntegrityError) NOT NULL constraint failed: jobs.id +[SQL: INSERT INTO jobs (cron_expr, handler_path, public_cfg, secret_cfg, enabled, last_run_at) VALUES (?, ?, ?, ?, ?, ?) RETURNING created_at, updated_at] +[parameters: ('* * * * *', 'extensions.example.job:ExampleJob', '{"name": "Mars"}', 'gAAAAABpWzE-9fVtEGO8ylcskZ3mtjWj8DiLCcUwgW3eCC3vvXv0DvGCWSFwkSc-h5UfJ5tpO8viLorBj6_z_Vh7mNW3OIhGjIVgpcorp9uXZhIjIiSJY_8=', 1, None)] +(Background on this error at: https://sqlalche.me/e/20/gkpj) +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context + self.dialect.do_execute( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute + cursor.execute(statement, parameters) +sqlite3.IntegrityError: NOT NULL constraint failed: jobs.id + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqladmin/application.py", line 545, in create + obj = await model_view.insert_model(request, form_data_dict) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/models.py", line 1069, in insert_model + return await Query(self).insert(data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 228, in insert + return await anyio.to_thread.run_sync(self._insert_sync, data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 61, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2525, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 986, in run + result = context.run(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 201, in _insert_sync + session.commit() + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2030, in commit + trans.commit(_to_root=True) + File "", line 2, in commit + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go + ret_value = fn(self, *arg, **kw) + ^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 1311, in commit + self._prepare_impl() + File "", line 2, in _prepare_impl + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go + ret_value = fn(self, *arg, **kw) + ^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 1286, in _prepare_impl + self.session.flush() + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 4331, in flush + self._flush(objects) + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 4466, in _flush + with util.safe_reraise(): + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/util/langhelpers.py", line 224, in __exit__ + raise exc_value.with_traceback(exc_tb) + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 4427, in _flush + flush_context.execute() + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/unitofwork.py", line 466, in execute + rec.execute(self) + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/unitofwork.py", line 642, in execute + util.preloaded.orm_persistence.save_obj( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/persistence.py", line 93, in save_obj + _emit_insert_statements( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/persistence.py", line 1233, in _emit_insert_statements + result = connection.execute( + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1419, in execute + return meth( + ^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 527, in _execute_on_connection + return connection._execute_clauseelement( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1641, in _execute_clauseelement + ret = self._execute_context( + ^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context + return self._exec_single_context( + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context + self._handle_dbapi_exception( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2363, in _handle_dbapi_exception + raise sqlalchemy_exception.with_traceback(exc_info[2]) from e + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context + self.dialect.do_execute( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute + cursor.execute(statement, parameters) +sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) NOT NULL constraint failed: jobs.id +[SQL: INSERT INTO jobs (cron_expr, handler_path, public_cfg, secret_cfg, enabled, last_run_at) VALUES (?, ?, ?, ?, ?, ?) RETURNING created_at, updated_at] +[parameters: ('* * * * *', 'extensions.example.job:ExampleJob', '{"name": "Mars"}', 'gAAAAABpWzE-9fVtEGO8ylcskZ3mtjWj8DiLCcUwgW3eCC3vvXv0DvGCWSFwkSc-h5UfJ5tpO8viLorBj6_z_Vh7mNW3OIhGjIVgpcorp9uXZhIjIiSJY_8=', 1, None)] +(Background on this error at: https://sqlalche.me/e/20/gkpj) +2026-01-05 03:40:22 ERROR sqladmin.application No module named 'app.integrations.base_client' +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqladmin/application.py", line 545, in create + obj = await model_view.insert_model(request, form_data_dict) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/models.py", line 1069, in insert_model + return await Query(self).insert(data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 228, in insert + return await anyio.to_thread.run_sync(self._insert_sync, data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 61, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2525, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 986, in run + result = context.run(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 196, in _insert_sync + anyio.from_thread.run( + File "/usr/local/lib/python3.11/site-packages/anyio/from_thread.py", line 90, in run + return token.backend_class.run_async_from_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2572, in run_async_from_thread + return f.result() + ^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 456, in result + return self.__get_result() + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result + raise self._exception + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2553, in task_wrapper + return await func(*args) + ^^^^^^^^^^^^^^^^^ + File "/app/app/admin/views.py", line 60, in on_model_change + load_job_class(str(handler_path).strip()) + File "/app/app/plugins/manager.py", line 33, in load_job_class + mod = importlib.import_module(ref.module) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1204, in _gcd_import + File "", line 1176, in _find_and_load + File "", line 1147, in _find_and_load_unlocked + File "", line 690, in _load_unlocked + File "", line 940, in exec_module + File "", line 241, in _call_with_frames_removed + File "/app/extensions/example/job.py", line 7, in + from extensions.example.client import ExampleClient + File "/app/extensions/example/client.py", line 3, in + from app.integrations.base import BaseClient + File "/app/app/integrations/__init__.py", line 5, in + from app.integrations.base_client import BaseClient +ModuleNotFoundError: No module named 'app.integrations.base_client' +2026-01-05 03:44:18 ERROR sqladmin.application No module named 'app.integrations.base_client' +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqladmin/application.py", line 545, in create + obj = await model_view.insert_model(request, form_data_dict) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/models.py", line 1069, in insert_model + return await Query(self).insert(data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 228, in insert + return await anyio.to_thread.run_sync(self._insert_sync, data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 61, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2525, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 986, in run + result = context.run(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 196, in _insert_sync + anyio.from_thread.run( + File "/usr/local/lib/python3.11/site-packages/anyio/from_thread.py", line 90, in run + return token.backend_class.run_async_from_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2572, in run_async_from_thread + return f.result() + ^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 456, in result + return self.__get_result() + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result + raise self._exception + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2553, in task_wrapper + return await func(*args) + ^^^^^^^^^^^^^^^^^ + File "/app/app/admin/views.py", line 60, in on_model_change + load_job_class(str(handler_path).strip()) + File "/app/app/plugins/manager.py", line 33, in load_job_class + mod = importlib.import_module(ref.module) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1204, in _gcd_import + File "", line 1176, in _find_and_load + File "", line 1147, in _find_and_load_unlocked + File "", line 690, in _load_unlocked + File "", line 940, in exec_module + File "", line 241, in _call_with_frames_removed + File "/app/extensions/example/job.py", line 7, in + from extensions.example.client import ExampleClient + File "/app/extensions/example/client.py", line 3, in + from app.integrations.base import BaseClient + File "/app/app/integrations/__init__.py", line 5, in + from app.integrations.base_client import BaseClient +ModuleNotFoundError: No module named 'app.integrations.base_client' +2026-01-05 03:44:32 ERROR sqladmin.application No module named 'app.integrations.base_client' +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqladmin/application.py", line 545, in create + obj = await model_view.insert_model(request, form_data_dict) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/models.py", line 1069, in insert_model + return await Query(self).insert(data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 228, in insert + return await anyio.to_thread.run_sync(self._insert_sync, data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 61, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2525, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 986, in run + result = context.run(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 196, in _insert_sync + anyio.from_thread.run( + File "/usr/local/lib/python3.11/site-packages/anyio/from_thread.py", line 90, in run + return token.backend_class.run_async_from_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2572, in run_async_from_thread + return f.result() + ^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 456, in result + return self.__get_result() + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result + raise self._exception + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2553, in task_wrapper + return await func(*args) + ^^^^^^^^^^^^^^^^^ + File "/app/app/admin/views.py", line 60, in on_model_change + load_job_class(str(handler_path).strip()) + File "/app/app/plugins/manager.py", line 33, in load_job_class + mod = importlib.import_module(ref.module) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1204, in _gcd_import + File "", line 1176, in _find_and_load + File "", line 1147, in _find_and_load_unlocked + File "", line 690, in _load_unlocked + File "", line 940, in exec_module + File "", line 241, in _call_with_frames_removed + File "/app/extensions/example/job.py", line 7, in + from extensions.example.client import ExampleClient + File "/app/extensions/example/client.py", line 3, in + from app.integrations.base import BaseClient + File "/app/app/integrations/__init__.py", line 5, in + from app.integrations.base_client import BaseClient +ModuleNotFoundError: No module named 'app.integrations.base_client' +2026-01-05 03:45:45 ERROR sqladmin.application No module named 'app.integrations.base_client' +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqladmin/application.py", line 545, in create + obj = await model_view.insert_model(request, form_data_dict) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/models.py", line 1069, in insert_model + return await Query(self).insert(data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 228, in insert + return await anyio.to_thread.run_sync(self._insert_sync, data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 61, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2525, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 986, in run + result = context.run(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 196, in _insert_sync + anyio.from_thread.run( + File "/usr/local/lib/python3.11/site-packages/anyio/from_thread.py", line 90, in run + return token.backend_class.run_async_from_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2572, in run_async_from_thread + return f.result() + ^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 456, in result + return self.__get_result() + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result + raise self._exception + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2553, in task_wrapper + return await func(*args) + ^^^^^^^^^^^^^^^^^ + File "/app/app/admin/views.py", line 60, in on_model_change + load_job_class(str(handler_path).strip()) + File "/app/app/plugins/manager.py", line 33, in load_job_class + mod = importlib.import_module(ref.module) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1204, in _gcd_import + File "", line 1176, in _find_and_load + File "", line 1147, in _find_and_load_unlocked + File "", line 690, in _load_unlocked + File "", line 940, in exec_module + File "", line 241, in _call_with_frames_removed + File "/app/extensions/example/job.py", line 7, in + from extensions.example.client import ExampleClient + File "/app/extensions/example/client.py", line 3, in + from app.integrations.base import BaseClient + File "/app/app/integrations/__init__.py", line 5, in + from app.integrations.base_client import BaseClient +ModuleNotFoundError: No module named 'app.integrations.base_client' +2026-01-05 03:45:47 ERROR sqladmin.application No module named 'app.integrations.base_client' +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqladmin/application.py", line 545, in create + obj = await model_view.insert_model(request, form_data_dict) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/models.py", line 1069, in insert_model + return await Query(self).insert(data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 228, in insert + return await anyio.to_thread.run_sync(self._insert_sync, data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 61, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2525, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 986, in run + result = context.run(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 196, in _insert_sync + anyio.from_thread.run( + File "/usr/local/lib/python3.11/site-packages/anyio/from_thread.py", line 90, in run + return token.backend_class.run_async_from_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2572, in run_async_from_thread + return f.result() + ^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 456, in result + return self.__get_result() + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result + raise self._exception + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2553, in task_wrapper + return await func(*args) + ^^^^^^^^^^^^^^^^^ + File "/app/app/admin/views.py", line 60, in on_model_change + load_job_class(str(handler_path).strip()) + File "/app/app/plugins/manager.py", line 33, in load_job_class + mod = importlib.import_module(ref.module) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1204, in _gcd_import + File "", line 1176, in _find_and_load + File "", line 1147, in _find_and_load_unlocked + File "", line 690, in _load_unlocked + File "", line 940, in exec_module + File "", line 241, in _call_with_frames_removed + File "/app/extensions/example/job.py", line 7, in + from extensions.example.client import ExampleClient + File "/app/extensions/example/client.py", line 3, in + from app.integrations.base import BaseClient + File "/app/app/integrations/__init__.py", line 5, in + from app.integrations.base_client import BaseClient +ModuleNotFoundError: No module named 'app.integrations.base_client' +2026-01-05 03:45:49 ERROR sqladmin.application No module named 'app.integrations.base_client' +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqladmin/application.py", line 545, in create + obj = await model_view.insert_model(request, form_data_dict) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/models.py", line 1069, in insert_model + return await Query(self).insert(data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 228, in insert + return await anyio.to_thread.run_sync(self._insert_sync, data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 61, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2525, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 986, in run + result = context.run(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 196, in _insert_sync + anyio.from_thread.run( + File "/usr/local/lib/python3.11/site-packages/anyio/from_thread.py", line 90, in run + return token.backend_class.run_async_from_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2572, in run_async_from_thread + return f.result() + ^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 456, in result + return self.__get_result() + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result + raise self._exception + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2553, in task_wrapper + return await func(*args) + ^^^^^^^^^^^^^^^^^ + File "/app/app/admin/views.py", line 60, in on_model_change + load_job_class(str(handler_path).strip()) + File "/app/app/plugins/manager.py", line 33, in load_job_class + mod = importlib.import_module(ref.module) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1204, in _gcd_import + File "", line 1176, in _find_and_load + File "", line 1147, in _find_and_load_unlocked + File "", line 690, in _load_unlocked + File "", line 940, in exec_module + File "", line 241, in _call_with_frames_removed + File "/app/extensions/example/job.py", line 7, in + from extensions.example.client import ExampleClient + File "/app/extensions/example/client.py", line 3, in + from app.integrations.base import BaseClient + File "/app/app/integrations/__init__.py", line 5, in + from app.integrations.base_client import BaseClient +ModuleNotFoundError: No module named 'app.integrations.base_client' +2026-01-05 03:46:28 ERROR sqladmin.application No module named 'app.integrations.base_client' +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqladmin/application.py", line 545, in create + obj = await model_view.insert_model(request, form_data_dict) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/models.py", line 1069, in insert_model + return await Query(self).insert(data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 228, in insert + return await anyio.to_thread.run_sync(self._insert_sync, data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 61, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2525, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 986, in run + result = context.run(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 196, in _insert_sync + anyio.from_thread.run( + File "/usr/local/lib/python3.11/site-packages/anyio/from_thread.py", line 90, in run + return token.backend_class.run_async_from_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2572, in run_async_from_thread + return f.result() + ^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 456, in result + return self.__get_result() + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result + raise self._exception + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2553, in task_wrapper + return await func(*args) + ^^^^^^^^^^^^^^^^^ + File "/app/app/admin/views.py", line 60, in on_model_change + load_job_class(str(handler_path).strip()) + File "/app/app/plugins/manager.py", line 33, in load_job_class + mod = importlib.import_module(ref.module) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1204, in _gcd_import + File "", line 1176, in _find_and_load + File "", line 1147, in _find_and_load_unlocked + File "", line 690, in _load_unlocked + File "", line 940, in exec_module + File "", line 241, in _call_with_frames_removed + File "/app/extensions/example/job.py", line 7, in + from extensions.example.client import ExampleClient + File "/app/extensions/example/client.py", line 3, in + from app.integrations.base import BaseClient + File "/app/app/integrations/__init__.py", line 5, in + from app.integrations.base_client import BaseClient +ModuleNotFoundError: No module named 'app.integrations.base_client' +2026-01-05 03:46:30 ERROR sqladmin.application No module named 'app.integrations.base_client' +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqladmin/application.py", line 545, in create + obj = await model_view.insert_model(request, form_data_dict) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/models.py", line 1069, in insert_model + return await Query(self).insert(data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 228, in insert + return await anyio.to_thread.run_sync(self._insert_sync, data, request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 61, in run_sync + return await get_async_backend().run_sync_in_worker_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2525, in run_sync_in_worker_thread + return await future + ^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 986, in run + result = context.run(func, *args) + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqladmin/_queries.py", line 196, in _insert_sync + anyio.from_thread.run( + File "/usr/local/lib/python3.11/site-packages/anyio/from_thread.py", line 90, in run + return token.backend_class.run_async_from_thread( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2572, in run_async_from_thread + return f.result() + ^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 456, in result + return self.__get_result() + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result + raise self._exception + File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 2553, in task_wrapper + return await func(*args) + ^^^^^^^^^^^^^^^^^ + File "/app/app/admin/views.py", line 60, in on_model_change + load_job_class(str(handler_path).strip()) + File "/app/app/plugins/manager.py", line 33, in load_job_class + mod = importlib.import_module(ref.module) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1204, in _gcd_import + File "", line 1176, in _find_and_load + File "", line 1147, in _find_and_load_unlocked + File "", line 690, in _load_unlocked + File "", line 940, in exec_module + File "", line 241, in _call_with_frames_removed + File "/app/extensions/example/job.py", line 7, in + from extensions.example.client import ExampleClient + File "/app/extensions/example/client.py", line 3, in + from app.integrations.base import BaseClient + File "/app/app/integrations/__init__.py", line 5, in + from app.integrations.base_client import BaseClient +ModuleNotFoundError: No module named 'app.integrations.base_client' +2026-01-05 04:04:28 WARNING celery.redirected 2026-01-05 04:04:28 INFO celery.app.trace Task connecthub.dispatcher.tick[9bc692b9-408d-408d-9b78-f4c8fd3bf910] succeeded in 0.0407933510005023s: {'triggered': 1} +2026-01-05 04:04:28 INFO celery.app.trace Task connecthub.dispatcher.tick[9bc692b9-408d-408d-9b78-f4c8fd3bf910] succeeded in 0.0407933510005023s: {'triggered': 1} +2026-01-05 04:04:28 WARNING celery.redirected 2026-01-05 04:04:28 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:04:28 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:04:28 WARNING celery.redirected 2026-01-05 04:04:28 INFO celery.app.trace Task connecthub.execute_job[a935d18c-a886-41b6-96ed-2b47f332c024] succeeded in 0.12210913499984599s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:04:28 INFO celery.app.trace Task connecthub.execute_job[a935d18c-a886-41b6-96ed-2b47f332c024] succeeded in 0.12210913499984599s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:05:28 WARNING celery.redirected 2026-01-05 04:05:28 INFO celery.app.trace Task connecthub.dispatcher.tick[3dce6292-6dc0-4542-90b5-6714164b0903] succeeded in 0.0315388979997806s: {'triggered': 1} +2026-01-05 04:05:28 INFO celery.app.trace Task connecthub.dispatcher.tick[3dce6292-6dc0-4542-90b5-6714164b0903] succeeded in 0.0315388979997806s: {'triggered': 1} +2026-01-05 04:05:28 WARNING celery.redirected 2026-01-05 04:05:28 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:05:28 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:05:28 WARNING celery.redirected 2026-01-05 04:05:28 INFO celery.app.trace Task connecthub.execute_job[e4cd35f4-a5c8-4f09-8c4c-febb2fe9070f] succeeded in 0.033638812999924994s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:05:28 INFO celery.app.trace Task connecthub.execute_job[e4cd35f4-a5c8-4f09-8c4c-febb2fe9070f] succeeded in 0.033638812999924994s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:06:28 WARNING celery.redirected 2026-01-05 04:06:28 INFO celery.app.trace Task connecthub.dispatcher.tick[ce923b69-80a1-465f-9f55-f5931cd6b95d] succeeded in 0.013310617000570346s: {'triggered': 1} +2026-01-05 04:06:28 INFO celery.app.trace Task connecthub.dispatcher.tick[ce923b69-80a1-465f-9f55-f5931cd6b95d] succeeded in 0.013310617000570346s: {'triggered': 1} +2026-01-05 04:06:28 WARNING celery.redirected 2026-01-05 04:06:28 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:06:28 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:06:28 WARNING celery.redirected 2026-01-05 04:06:28 INFO celery.app.trace Task connecthub.execute_job[98de0f60-76aa-4465-8d1a-89d3bdd47669] succeeded in 0.024751318000198808s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:06:28 INFO celery.app.trace Task connecthub.execute_job[98de0f60-76aa-4465-8d1a-89d3bdd47669] succeeded in 0.024751318000198808s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:07:28 WARNING celery.redirected 2026-01-05 04:07:28 INFO celery.app.trace Task connecthub.dispatcher.tick[39fcfaeb-9b76-487f-b913-a5fbc64d5f72] succeeded in 0.01909061300011672s: {'triggered': 1} +2026-01-05 04:07:28 INFO celery.app.trace Task connecthub.dispatcher.tick[39fcfaeb-9b76-487f-b913-a5fbc64d5f72] succeeded in 0.01909061300011672s: {'triggered': 1} +2026-01-05 04:07:28 WARNING celery.redirected 2026-01-05 04:07:28 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:07:28 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:07:28 WARNING celery.redirected 2026-01-05 04:07:28 INFO celery.app.trace Task connecthub.execute_job[8660dfdd-cea6-4d39-8113-bab1948b4d14] succeeded in 0.02584623399980046s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:07:28 INFO celery.app.trace Task connecthub.execute_job[8660dfdd-cea6-4d39-8113-bab1948b4d14] succeeded in 0.02584623399980046s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:08:28 WARNING celery.redirected 2026-01-05 04:08:28 INFO celery.app.trace Task connecthub.dispatcher.tick[04377e32-ddf9-4fc3-96ae-b1501be248a1] succeeded in 0.008148328000061156s: {'triggered': 1} +2026-01-05 04:08:28 INFO celery.app.trace Task connecthub.dispatcher.tick[04377e32-ddf9-4fc3-96ae-b1501be248a1] succeeded in 0.008148328000061156s: {'triggered': 1} +2026-01-05 04:08:28 WARNING celery.redirected 2026-01-05 04:08:28 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:08:28 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:08:28 WARNING celery.redirected 2026-01-05 04:08:28 INFO celery.app.trace Task connecthub.execute_job[911b3014-5025-417d-87d1-16f29fd04adc] succeeded in 0.01953678000063519s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:08:28 INFO celery.app.trace Task connecthub.execute_job[911b3014-5025-417d-87d1-16f29fd04adc] succeeded in 0.01953678000063519s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:09:52 WARNING celery.redirected 2026-01-05 04:09:52 INFO celery.app.trace Task connecthub.dispatcher.tick[979e1130-624f-46a0-8527-90a545d94d8d] succeeded in 0.022409070000321663s: {'triggered': 1} +2026-01-05 04:09:52 INFO celery.app.trace Task connecthub.dispatcher.tick[979e1130-624f-46a0-8527-90a545d94d8d] succeeded in 0.022409070000321663s: {'triggered': 1} +2026-01-05 04:09:52 WARNING celery.redirected 2026-01-05 04:09:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:09:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:09:52 WARNING celery.redirected 2026-01-05 04:09:52 INFO celery.app.trace Task connecthub.execute_job[a8643351-b8b3-4731-a0c9-b37e6e80d2c0] succeeded in 0.06366400299975794s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:09:52 INFO celery.app.trace Task connecthub.execute_job[a8643351-b8b3-4731-a0c9-b37e6e80d2c0] succeeded in 0.06366400299975794s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:10:52 WARNING celery.redirected 2026-01-05 04:10:52 INFO celery.app.trace Task connecthub.dispatcher.tick[b9d03cc4-9cfd-42cc-a24a-7ee65223a7b5] succeeded in 0.021748904000560287s: {'triggered': 1} +2026-01-05 04:10:52 INFO celery.app.trace Task connecthub.dispatcher.tick[b9d03cc4-9cfd-42cc-a24a-7ee65223a7b5] succeeded in 0.021748904000560287s: {'triggered': 1} +2026-01-05 04:10:52 WARNING celery.redirected 2026-01-05 04:10:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:10:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:10:52 WARNING celery.redirected 2026-01-05 04:10:52 INFO celery.app.trace Task connecthub.execute_job[2fc70227-c028-47a9-928e-fbe207426125] succeeded in 0.030770231999667885s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:10:52 INFO celery.app.trace Task connecthub.execute_job[2fc70227-c028-47a9-928e-fbe207426125] succeeded in 0.030770231999667885s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:11:52 WARNING celery.redirected 2026-01-05 04:11:52 INFO celery.app.trace Task connecthub.dispatcher.tick[de3cf52c-cae5-4c51-ab29-c4ee50bbecf9] succeeded in 0.024469609999869135s: {'triggered': 1} +2026-01-05 04:11:52 INFO celery.app.trace Task connecthub.dispatcher.tick[de3cf52c-cae5-4c51-ab29-c4ee50bbecf9] succeeded in 0.024469609999869135s: {'triggered': 1} +2026-01-05 04:11:52 WARNING celery.redirected 2026-01-05 04:11:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:11:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:11:52 WARNING celery.redirected 2026-01-05 04:11:52 INFO celery.app.trace Task connecthub.execute_job[c116ba10-ffd1-4b26-96fa-f4acd3bd6970] succeeded in 0.028366817000460287s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:11:52 INFO celery.app.trace Task connecthub.execute_job[c116ba10-ffd1-4b26-96fa-f4acd3bd6970] succeeded in 0.028366817000460287s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:12:52 WARNING celery.redirected 2026-01-05 04:12:52 INFO celery.app.trace Task connecthub.dispatcher.tick[b35d28d2-9091-40b0-868f-037eb604eba0] succeeded in 0.020914112999889767s: {'triggered': 1} +2026-01-05 04:12:52 INFO celery.app.trace Task connecthub.dispatcher.tick[b35d28d2-9091-40b0-868f-037eb604eba0] succeeded in 0.020914112999889767s: {'triggered': 1} +2026-01-05 04:12:52 WARNING celery.redirected 2026-01-05 04:12:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:12:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:12:52 WARNING celery.redirected 2026-01-05 04:12:52 INFO celery.app.trace Task connecthub.execute_job[d3f53b27-df98-4a3c-b301-cb9bb21c1ec5] succeeded in 0.023244652999892423s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:12:52 INFO celery.app.trace Task connecthub.execute_job[d3f53b27-df98-4a3c-b301-cb9bb21c1ec5] succeeded in 0.023244652999892423s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:13:52 WARNING celery.redirected 2026-01-05 04:13:52 INFO celery.app.trace Task connecthub.dispatcher.tick[4e920d4e-f8d0-4fc5-b993-48ded04e6358] succeeded in 0.013280492000376398s: {'triggered': 1} +2026-01-05 04:13:52 INFO celery.app.trace Task connecthub.dispatcher.tick[4e920d4e-f8d0-4fc5-b993-48ded04e6358] succeeded in 0.013280492000376398s: {'triggered': 1} +2026-01-05 04:13:52 WARNING celery.redirected 2026-01-05 04:13:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:13:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:13:52 WARNING celery.redirected 2026-01-05 04:13:52 INFO celery.app.trace Task connecthub.execute_job[048f0b9d-99b1-4521-aca1-5fd1f6b1c5da] succeeded in 0.02696423399993364s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:13:52 INFO celery.app.trace Task connecthub.execute_job[048f0b9d-99b1-4521-aca1-5fd1f6b1c5da] succeeded in 0.02696423399993364s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:14:10 WARNING celery.redirected 2026-01-05 04:14:10 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:14:10 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:14:10 WARNING celery.redirected 2026-01-05 04:14:10 INFO celery.app.trace Task connecthub.execute_job[54b32418-34f0-43b6-9177-94d0447424ad] succeeded in 0.13937025099949096s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:14:10 INFO celery.app.trace Task connecthub.execute_job[54b32418-34f0-43b6-9177-94d0447424ad] succeeded in 0.13937025099949096s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:14:25 WARNING celery.redirected 2026-01-05 04:14:25 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:14:25 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:14:25 WARNING celery.redirected 2026-01-05 04:14:25 INFO celery.app.trace Task connecthub.execute_job[872f03bb-3828-418f-ba3e-e5d1c8722967] succeeded in 0.02000432200020441s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:14:25 INFO celery.app.trace Task connecthub.execute_job[872f03bb-3828-418f-ba3e-e5d1c8722967] succeeded in 0.02000432200020441s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:14:36 WARNING celery.redirected 2026-01-05 04:14:36 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:14:36 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:14:36 WARNING celery.redirected 2026-01-05 04:14:36 INFO celery.app.trace Task connecthub.execute_job[4ff44d2e-2a1d-4883-9b00-f3abe7c47e1e] succeeded in 0.01975427999968815s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:14:36 INFO celery.app.trace Task connecthub.execute_job[4ff44d2e-2a1d-4883-9b00-f3abe7c47e1e] succeeded in 0.01975427999968815s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:14:52 WARNING celery.redirected 2026-01-05 04:14:52 INFO celery.app.trace Task connecthub.dispatcher.tick[3c3f00c6-c41c-4f21-b5e2-41836b133e71] succeeded in 0.01676345899977605s: {'triggered': 1} +2026-01-05 04:14:52 INFO celery.app.trace Task connecthub.dispatcher.tick[3c3f00c6-c41c-4f21-b5e2-41836b133e71] succeeded in 0.01676345899977605s: {'triggered': 1} +2026-01-05 04:14:52 WARNING celery.redirected 2026-01-05 04:14:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:14:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:14:52 WARNING celery.redirected 2026-01-05 04:14:52 INFO celery.app.trace Task connecthub.execute_job[1363d33d-d6a4-457c-9d2e-ec52fb141b6f] succeeded in 0.0199828750000961s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:14:52 INFO celery.app.trace Task connecthub.execute_job[1363d33d-d6a4-457c-9d2e-ec52fb141b6f] succeeded in 0.0199828750000961s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:15:52 WARNING celery.redirected 2026-01-05 04:15:52 INFO celery.app.trace Task connecthub.dispatcher.tick[ea94b6c0-0c57-43a5-9e24-36009713b597] succeeded in 0.039750081999955s: {'triggered': 1} +2026-01-05 04:15:52 INFO celery.app.trace Task connecthub.dispatcher.tick[ea94b6c0-0c57-43a5-9e24-36009713b597] succeeded in 0.039750081999955s: {'triggered': 1} +2026-01-05 04:15:52 WARNING celery.redirected 2026-01-05 04:15:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:15:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:15:52 WARNING celery.redirected 2026-01-05 04:15:52 INFO celery.app.trace Task connecthub.execute_job[6c71ecc2-735f-43d4-8c67-762ec088cd84] succeeded in 0.027154014999723586s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:15:52 INFO celery.app.trace Task connecthub.execute_job[6c71ecc2-735f-43d4-8c67-762ec088cd84] succeeded in 0.027154014999723586s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:16:52 WARNING celery.redirected 2026-01-05 04:16:52 INFO celery.app.trace Task connecthub.dispatcher.tick[d838ad8f-16ab-465a-8cef-a50eb6f684ac] succeeded in 0.010788003000016033s: {'triggered': 1} +2026-01-05 04:16:52 INFO celery.app.trace Task connecthub.dispatcher.tick[d838ad8f-16ab-465a-8cef-a50eb6f684ac] succeeded in 0.010788003000016033s: {'triggered': 1} +2026-01-05 04:16:52 WARNING celery.redirected 2026-01-05 04:16:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:16:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:16:52 WARNING celery.redirected 2026-01-05 04:16:52 INFO celery.app.trace Task connecthub.execute_job[e7096c78-a712-403f-82df-fdaea7af897e] succeeded in 0.02174886400007381s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:16:52 INFO celery.app.trace Task connecthub.execute_job[e7096c78-a712-403f-82df-fdaea7af897e] succeeded in 0.02174886400007381s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:17:52 WARNING celery.redirected 2026-01-05 04:17:52 INFO celery.app.trace Task connecthub.dispatcher.tick[18bf30ca-3980-4607-8bef-e69f39bdcb48] succeeded in 0.01799064599981648s: {'triggered': 1} +2026-01-05 04:17:52 INFO celery.app.trace Task connecthub.dispatcher.tick[18bf30ca-3980-4607-8bef-e69f39bdcb48] succeeded in 0.01799064599981648s: {'triggered': 1} +2026-01-05 04:17:52 WARNING celery.redirected 2026-01-05 04:17:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:17:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:17:52 WARNING celery.redirected 2026-01-05 04:17:52 INFO celery.app.trace Task connecthub.execute_job[c59c5f51-8e7d-4e86-b70e-22b2d30ab229] succeeded in 0.021925345999989077s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:17:52 INFO celery.app.trace Task connecthub.execute_job[c59c5f51-8e7d-4e86-b70e-22b2d30ab229] succeeded in 0.021925345999989077s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:18:52 WARNING celery.redirected 2026-01-05 04:18:52 INFO celery.app.trace Task connecthub.dispatcher.tick[2d1b5ff3-9939-4cb6-a807-8403c4052fea] succeeded in 0.013237076000223169s: {'triggered': 1} +2026-01-05 04:18:52 INFO celery.app.trace Task connecthub.dispatcher.tick[2d1b5ff3-9939-4cb6-a807-8403c4052fea] succeeded in 0.013237076000223169s: {'triggered': 1} +2026-01-05 04:18:52 WARNING celery.redirected 2026-01-05 04:18:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:18:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:18:52 WARNING celery.redirected 2026-01-05 04:18:52 INFO celery.app.trace Task connecthub.execute_job[cda83fcd-ee9f-4df1-b3fd-35e9a4494e56] succeeded in 0.026179608999882475s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:18:52 INFO celery.app.trace Task connecthub.execute_job[cda83fcd-ee9f-4df1-b3fd-35e9a4494e56] succeeded in 0.026179608999882475s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:19:52 WARNING celery.redirected 2026-01-05 04:19:52 INFO celery.app.trace Task connecthub.dispatcher.tick[00f813c1-b361-4697-acbb-1c5f9a455ef4] succeeded in 0.01128316199992696s: {'triggered': 1} +2026-01-05 04:19:52 INFO celery.app.trace Task connecthub.dispatcher.tick[00f813c1-b361-4697-acbb-1c5f9a455ef4] succeeded in 0.01128316199992696s: {'triggered': 1} +2026-01-05 04:19:52 WARNING celery.redirected 2026-01-05 04:19:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:19:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:19:52 WARNING celery.redirected 2026-01-05 04:19:52 INFO celery.app.trace Task connecthub.execute_job[a95a06f1-f624-4258-bb1a-14f3edf5fd23] succeeded in 0.024273901999549707s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:19:52 INFO celery.app.trace Task connecthub.execute_job[a95a06f1-f624-4258-bb1a-14f3edf5fd23] succeeded in 0.024273901999549707s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:20:52 WARNING celery.redirected 2026-01-05 04:20:52 INFO celery.app.trace Task connecthub.dispatcher.tick[ad6b6f35-620d-4f15-885f-eed710c33afa] succeeded in 0.010243423000247276s: {'triggered': 1} +2026-01-05 04:20:52 INFO celery.app.trace Task connecthub.dispatcher.tick[ad6b6f35-620d-4f15-885f-eed710c33afa] succeeded in 0.010243423000247276s: {'triggered': 1} +2026-01-05 04:20:52 WARNING celery.redirected 2026-01-05 04:20:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:20:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:20:52 WARNING celery.redirected 2026-01-05 04:20:52 INFO celery.app.trace Task connecthub.execute_job[05deae7a-df77-4dbe-aaa9-afb2c05ee3d8] succeeded in 0.018025465999926382s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:20:52 INFO celery.app.trace Task connecthub.execute_job[05deae7a-df77-4dbe-aaa9-afb2c05ee3d8] succeeded in 0.018025465999926382s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:21:52 WARNING celery.redirected 2026-01-05 04:21:52 INFO celery.app.trace Task connecthub.dispatcher.tick[90e7750e-071b-4521-b41e-8b9795bf58f9] succeeded in 0.021846335999725852s: {'triggered': 1} +2026-01-05 04:21:52 INFO celery.app.trace Task connecthub.dispatcher.tick[90e7750e-071b-4521-b41e-8b9795bf58f9] succeeded in 0.021846335999725852s: {'triggered': 1} +2026-01-05 04:21:52 WARNING celery.redirected 2026-01-05 04:21:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:21:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:21:52 WARNING celery.redirected 2026-01-05 04:21:52 INFO celery.app.trace Task connecthub.execute_job[42f323b6-ebdd-476e-908a-8facd47b94cc] succeeded in 0.030602228999669023s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:21:52 INFO celery.app.trace Task connecthub.execute_job[42f323b6-ebdd-476e-908a-8facd47b94cc] succeeded in 0.030602228999669023s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:22:52 WARNING celery.redirected 2026-01-05 04:22:52 INFO celery.app.trace Task connecthub.dispatcher.tick[14cd9189-733f-4b27-b2a0-07f8f943e537] succeeded in 0.028103550999730942s: {'triggered': 1} +2026-01-05 04:22:52 INFO celery.app.trace Task connecthub.dispatcher.tick[14cd9189-733f-4b27-b2a0-07f8f943e537] succeeded in 0.028103550999730942s: {'triggered': 1} +2026-01-05 04:22:52 WARNING celery.redirected 2026-01-05 04:22:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:22:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:22:52 WARNING celery.redirected 2026-01-05 04:22:52 INFO celery.app.trace Task connecthub.execute_job[7f5bb270-619c-46d6-a372-a740061f4c98] succeeded in 0.03812834600012138s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:22:52 INFO celery.app.trace Task connecthub.execute_job[7f5bb270-619c-46d6-a372-a740061f4c98] succeeded in 0.03812834600012138s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:23:52 WARNING celery.redirected 2026-01-05 04:23:52 INFO celery.app.trace Task connecthub.dispatcher.tick[19876782-ab7f-408a-92f9-e3ffb2500466] succeeded in 0.020140034000178275s: {'triggered': 1} +2026-01-05 04:23:52 INFO celery.app.trace Task connecthub.dispatcher.tick[19876782-ab7f-408a-92f9-e3ffb2500466] succeeded in 0.020140034000178275s: {'triggered': 1} +2026-01-05 04:23:52 WARNING celery.redirected 2026-01-05 04:23:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:23:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:23:52 WARNING celery.redirected 2026-01-05 04:23:52 INFO celery.app.trace Task connecthub.execute_job[7066324a-67f4-4697-8626-e71c18586633] succeeded in 0.026626016999216517s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:23:52 INFO celery.app.trace Task connecthub.execute_job[7066324a-67f4-4697-8626-e71c18586633] succeeded in 0.026626016999216517s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:24:52 WARNING celery.redirected 2026-01-05 04:24:52 INFO celery.app.trace Task connecthub.dispatcher.tick[e42a4486-48d4-4841-8035-d9cd341054f2] succeeded in 0.06983272399975249s: {'triggered': 1} +2026-01-05 04:24:52 INFO celery.app.trace Task connecthub.dispatcher.tick[e42a4486-48d4-4841-8035-d9cd341054f2] succeeded in 0.06983272399975249s: {'triggered': 1} +2026-01-05 04:24:52 WARNING celery.redirected 2026-01-05 04:24:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:24:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:24:52 WARNING celery.redirected 2026-01-05 04:24:52 INFO celery.app.trace Task connecthub.execute_job[11610950-599b-4d91-9c87-915122c6ad2d] succeeded in 0.1087242449993937s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:24:52 INFO celery.app.trace Task connecthub.execute_job[11610950-599b-4d91-9c87-915122c6ad2d] succeeded in 0.1087242449993937s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:25:52 WARNING celery.redirected 2026-01-05 04:25:52 INFO celery.app.trace Task connecthub.dispatcher.tick[90453c9f-f26f-4f1d-b766-1867b8a5c97f] succeeded in 0.028080609000426193s: {'triggered': 1} +2026-01-05 04:25:52 INFO celery.app.trace Task connecthub.dispatcher.tick[90453c9f-f26f-4f1d-b766-1867b8a5c97f] succeeded in 0.028080609000426193s: {'triggered': 1} +2026-01-05 04:25:52 WARNING celery.redirected 2026-01-05 04:25:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:25:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:25:52 WARNING celery.redirected 2026-01-05 04:25:52 INFO celery.app.trace Task connecthub.execute_job[f36fd7ea-12bd-4ce5-93b1-105708409a5e] succeeded in 0.024148027000592265s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:25:52 INFO celery.app.trace Task connecthub.execute_job[f36fd7ea-12bd-4ce5-93b1-105708409a5e] succeeded in 0.024148027000592265s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:26:52 WARNING celery.redirected 2026-01-05 04:26:52 INFO celery.app.trace Task connecthub.dispatcher.tick[1bfe4027-d3a1-4f14-9a5e-bd08d95f84a0] succeeded in 0.021505861999685294s: {'triggered': 1} +2026-01-05 04:26:52 INFO celery.app.trace Task connecthub.dispatcher.tick[1bfe4027-d3a1-4f14-9a5e-bd08d95f84a0] succeeded in 0.021505861999685294s: {'triggered': 1} +2026-01-05 04:26:52 WARNING celery.redirected 2026-01-05 04:26:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:26:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:26:52 WARNING celery.redirected 2026-01-05 04:26:52 INFO celery.app.trace Task connecthub.execute_job[a0cb31bd-78bd-4638-87f9-3b227d8597cc] succeeded in 0.039379184999233985s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:26:52 INFO celery.app.trace Task connecthub.execute_job[a0cb31bd-78bd-4638-87f9-3b227d8597cc] succeeded in 0.039379184999233985s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:27:52 WARNING celery.redirected 2026-01-05 04:27:52 INFO celery.app.trace Task connecthub.dispatcher.tick[906707f0-7349-4277-87c7-d55568ad86ca] succeeded in 0.011816539999927045s: {'triggered': 1} +2026-01-05 04:27:52 INFO celery.app.trace Task connecthub.dispatcher.tick[906707f0-7349-4277-87c7-d55568ad86ca] succeeded in 0.011816539999927045s: {'triggered': 1} +2026-01-05 04:27:52 WARNING celery.redirected 2026-01-05 04:27:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:27:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:27:52 WARNING celery.redirected 2026-01-05 04:27:52 INFO celery.app.trace Task connecthub.execute_job[f9398f7f-a666-4530-9c99-ca5021858e2f] succeeded in 0.01940625900078885s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:27:52 INFO celery.app.trace Task connecthub.execute_job[f9398f7f-a666-4530-9c99-ca5021858e2f] succeeded in 0.01940625900078885s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:28:52 WARNING celery.redirected 2026-01-05 04:28:52 INFO celery.app.trace Task connecthub.dispatcher.tick[8085bae0-61bf-4f59-bd29-a6957f734744] succeeded in 0.012278085000616556s: {'triggered': 1} +2026-01-05 04:28:52 INFO celery.app.trace Task connecthub.dispatcher.tick[8085bae0-61bf-4f59-bd29-a6957f734744] succeeded in 0.012278085000616556s: {'triggered': 1} +2026-01-05 04:28:52 WARNING celery.redirected 2026-01-05 04:28:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:28:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:28:52 WARNING celery.redirected 2026-01-05 04:28:52 INFO celery.app.trace Task connecthub.execute_job[8aea880e-5cc6-4764-9661-feb878277ac7] succeeded in 0.02511479699933261s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:28:52 INFO celery.app.trace Task connecthub.execute_job[8aea880e-5cc6-4764-9661-feb878277ac7] succeeded in 0.02511479699933261s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:29:52 WARNING celery.redirected 2026-01-05 04:29:52 INFO celery.app.trace Task connecthub.dispatcher.tick[d55f31e4-9583-4310-b33e-05e0ff6f8fc5] succeeded in 0.017971582999962266s: {'triggered': 1} +2026-01-05 04:29:52 INFO celery.app.trace Task connecthub.dispatcher.tick[d55f31e4-9583-4310-b33e-05e0ff6f8fc5] succeeded in 0.017971582999962266s: {'triggered': 1} +2026-01-05 04:29:52 WARNING celery.redirected 2026-01-05 04:29:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:29:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:29:52 WARNING celery.redirected 2026-01-05 04:29:52 INFO celery.app.trace Task connecthub.execute_job[5db185f8-a01d-4ced-b0d8-a2fa8ca00939] succeeded in 0.01592598499973974s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:29:52 INFO celery.app.trace Task connecthub.execute_job[5db185f8-a01d-4ced-b0d8-a2fa8ca00939] succeeded in 0.01592598499973974s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:30:52 WARNING celery.redirected 2026-01-05 04:30:52 INFO celery.app.trace Task connecthub.dispatcher.tick[03996f49-dba2-4bb9-8ede-90fa614ffbfe] succeeded in 0.010479280999788898s: {'triggered': 1} +2026-01-05 04:30:52 INFO celery.app.trace Task connecthub.dispatcher.tick[03996f49-dba2-4bb9-8ede-90fa614ffbfe] succeeded in 0.010479280999788898s: {'triggered': 1} +2026-01-05 04:30:52 WARNING celery.redirected 2026-01-05 04:30:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:30:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:30:52 WARNING celery.redirected 2026-01-05 04:30:52 INFO celery.app.trace Task connecthub.execute_job[b5be6b84-78aa-4700-b692-fe705feb0bdd] succeeded in 0.028096860999539786s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:30:52 INFO celery.app.trace Task connecthub.execute_job[b5be6b84-78aa-4700-b692-fe705feb0bdd] succeeded in 0.028096860999539786s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:31:52 WARNING celery.redirected 2026-01-05 04:31:52 INFO celery.app.trace Task connecthub.dispatcher.tick[5956c67b-cfed-40f3-9911-f75211554056] succeeded in 0.010633400000187976s: {'triggered': 1} +2026-01-05 04:31:52 INFO celery.app.trace Task connecthub.dispatcher.tick[5956c67b-cfed-40f3-9911-f75211554056] succeeded in 0.010633400000187976s: {'triggered': 1} +2026-01-05 04:31:52 WARNING celery.redirected 2026-01-05 04:31:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:31:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:31:52 WARNING celery.redirected 2026-01-05 04:31:52 INFO celery.app.trace Task connecthub.execute_job[67814a34-9bcc-47c2-a1c6-331650627725] succeeded in 0.027063096000347286s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:31:52 INFO celery.app.trace Task connecthub.execute_job[67814a34-9bcc-47c2-a1c6-331650627725] succeeded in 0.027063096000347286s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:32:52 WARNING celery.redirected 2026-01-05 04:32:52 INFO celery.app.trace Task connecthub.dispatcher.tick[ed8dfe49-9fa7-464c-9174-b0f2106e0f0f] succeeded in 0.014456934000008914s: {'triggered': 1} +2026-01-05 04:32:52 INFO celery.app.trace Task connecthub.dispatcher.tick[ed8dfe49-9fa7-464c-9174-b0f2106e0f0f] succeeded in 0.014456934000008914s: {'triggered': 1} +2026-01-05 04:32:52 WARNING celery.redirected 2026-01-05 04:32:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:32:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:32:52 WARNING celery.redirected 2026-01-05 04:32:52 INFO celery.app.trace Task connecthub.execute_job[6ccdfdad-6589-4065-a311-8aba994d283d] succeeded in 0.026617454999723122s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:32:52 INFO celery.app.trace Task connecthub.execute_job[6ccdfdad-6589-4065-a311-8aba994d283d] succeeded in 0.026617454999723122s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:33:52 WARNING celery.redirected 2026-01-05 04:33:52 INFO celery.app.trace Task connecthub.dispatcher.tick[b35b0a9e-5936-4327-979c-a76459c11b4d] succeeded in 0.018993278999914764s: {'triggered': 1} +2026-01-05 04:33:52 INFO celery.app.trace Task connecthub.dispatcher.tick[b35b0a9e-5936-4327-979c-a76459c11b4d] succeeded in 0.018993278999914764s: {'triggered': 1} +2026-01-05 04:33:52 WARNING celery.redirected 2026-01-05 04:33:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:33:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:33:52 WARNING celery.redirected 2026-01-05 04:33:52 INFO celery.app.trace Task connecthub.execute_job[1d2fe071-4163-4b6d-83f0-762ec473f70d] succeeded in 0.031460221000088495s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:33:52 INFO celery.app.trace Task connecthub.execute_job[1d2fe071-4163-4b6d-83f0-762ec473f70d] succeeded in 0.031460221000088495s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:34:52 WARNING celery.redirected 2026-01-05 04:34:52 INFO celery.app.trace Task connecthub.dispatcher.tick[4afb4dfc-585f-4d52-861c-31b3da185db9] succeeded in 0.011016321000170137s: {'triggered': 1} +2026-01-05 04:34:52 INFO celery.app.trace Task connecthub.dispatcher.tick[4afb4dfc-585f-4d52-861c-31b3da185db9] succeeded in 0.011016321000170137s: {'triggered': 1} +2026-01-05 04:34:52 WARNING celery.redirected 2026-01-05 04:34:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:34:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:34:52 WARNING celery.redirected 2026-01-05 04:34:52 INFO celery.app.trace Task connecthub.execute_job[f509d0e3-b106-4e8b-9228-60e98f0a1539] succeeded in 0.0250381309997465s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:34:52 INFO celery.app.trace Task connecthub.execute_job[f509d0e3-b106-4e8b-9228-60e98f0a1539] succeeded in 0.0250381309997465s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:35:52 WARNING celery.redirected 2026-01-05 04:35:52 INFO celery.app.trace Task connecthub.dispatcher.tick[73d382ed-cea8-4c7f-bf79-2188ee912208] succeeded in 0.027740482000808697s: {'triggered': 1} +2026-01-05 04:35:52 INFO celery.app.trace Task connecthub.dispatcher.tick[73d382ed-cea8-4c7f-bf79-2188ee912208] succeeded in 0.027740482000808697s: {'triggered': 1} +2026-01-05 04:35:52 WARNING celery.redirected 2026-01-05 04:35:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:35:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:35:52 WARNING celery.redirected 2026-01-05 04:35:52 INFO celery.app.trace Task connecthub.execute_job[6821f78b-2f55-46c1-a499-1bba055688ad] succeeded in 0.026060805000270193s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:35:52 INFO celery.app.trace Task connecthub.execute_job[6821f78b-2f55-46c1-a499-1bba055688ad] succeeded in 0.026060805000270193s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:36:52 WARNING celery.redirected 2026-01-05 04:36:52 INFO celery.app.trace Task connecthub.dispatcher.tick[3781ba5b-938f-489c-a57e-8c3d0ee0715c] succeeded in 0.02685642399956123s: {'triggered': 1} +2026-01-05 04:36:52 INFO celery.app.trace Task connecthub.dispatcher.tick[3781ba5b-938f-489c-a57e-8c3d0ee0715c] succeeded in 0.02685642399956123s: {'triggered': 1} +2026-01-05 04:36:52 WARNING celery.redirected 2026-01-05 04:36:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:36:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:36:52 WARNING celery.redirected 2026-01-05 04:36:52 INFO celery.app.trace Task connecthub.execute_job[80d7c5f7-fed2-49d5-946e-ceb39b440163] succeeded in 0.029760638000880135s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:36:52 INFO celery.app.trace Task connecthub.execute_job[80d7c5f7-fed2-49d5-946e-ceb39b440163] succeeded in 0.029760638000880135s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:37:52 WARNING celery.redirected 2026-01-05 04:37:52 INFO celery.app.trace Task connecthub.dispatcher.tick[bee5c829-b099-49ab-a46d-a9c4c2fe9408] succeeded in 0.023322675999224884s: {'triggered': 1} +2026-01-05 04:37:52 INFO celery.app.trace Task connecthub.dispatcher.tick[bee5c829-b099-49ab-a46d-a9c4c2fe9408] succeeded in 0.023322675999224884s: {'triggered': 1} +2026-01-05 04:37:52 WARNING celery.redirected 2026-01-05 04:37:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:37:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:37:52 WARNING celery.redirected 2026-01-05 04:37:52 INFO celery.app.trace Task connecthub.execute_job[58efb57a-7f96-49c9-beb7-e1dd708ce499] succeeded in 0.02516265300073428s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:37:52 INFO celery.app.trace Task connecthub.execute_job[58efb57a-7f96-49c9-beb7-e1dd708ce499] succeeded in 0.02516265300073428s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:38:52 WARNING celery.redirected 2026-01-05 04:38:52 INFO celery.app.trace Task connecthub.dispatcher.tick[16f7dfc9-3204-4c36-9ee5-bb3c6ddeb0dc] succeeded in 0.01482132800083491s: {'triggered': 1} +2026-01-05 04:38:52 INFO celery.app.trace Task connecthub.dispatcher.tick[16f7dfc9-3204-4c36-9ee5-bb3c6ddeb0dc] succeeded in 0.01482132800083491s: {'triggered': 1} +2026-01-05 04:38:52 WARNING celery.redirected 2026-01-05 04:38:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:38:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:38:52 WARNING celery.redirected 2026-01-05 04:38:52 INFO celery.app.trace Task connecthub.execute_job[ab0cf7a1-f828-4fe3-92c7-8bdfac19e33c] succeeded in 0.026130655998713337s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:38:52 INFO celery.app.trace Task connecthub.execute_job[ab0cf7a1-f828-4fe3-92c7-8bdfac19e33c] succeeded in 0.026130655998713337s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:39:52 WARNING celery.redirected 2026-01-05 04:39:52 INFO celery.app.trace Task connecthub.dispatcher.tick[e5a17e48-f1e8-4fd3-b76a-1bae0c478408] succeeded in 0.033079069000450545s: {'triggered': 1} +2026-01-05 04:39:52 INFO celery.app.trace Task connecthub.dispatcher.tick[e5a17e48-f1e8-4fd3-b76a-1bae0c478408] succeeded in 0.033079069000450545s: {'triggered': 1} +2026-01-05 04:39:52 WARNING celery.redirected 2026-01-05 04:39:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:39:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:39:52 WARNING celery.redirected 2026-01-05 04:39:52 INFO celery.app.trace Task connecthub.execute_job[52f791fa-42ce-4d59-a6b8-00ab2a3e30f1] succeeded in 0.039908691998789436s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:39:52 INFO celery.app.trace Task connecthub.execute_job[52f791fa-42ce-4d59-a6b8-00ab2a3e30f1] succeeded in 0.039908691998789436s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:40:52 WARNING celery.redirected 2026-01-05 04:40:52 INFO celery.app.trace Task connecthub.dispatcher.tick[532aa528-db60-42a6-a932-a7e00570785c] succeeded in 0.011276440000074217s: {'triggered': 1} +2026-01-05 04:40:52 INFO celery.app.trace Task connecthub.dispatcher.tick[532aa528-db60-42a6-a932-a7e00570785c] succeeded in 0.011276440000074217s: {'triggered': 1} +2026-01-05 04:40:52 WARNING celery.redirected 2026-01-05 04:40:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:40:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:40:52 WARNING celery.redirected 2026-01-05 04:40:52 INFO celery.app.trace Task connecthub.execute_job[0e54f62e-e468-48af-9ed0-c95cbba339ee] succeeded in 0.02018032300111372s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:40:52 INFO celery.app.trace Task connecthub.execute_job[0e54f62e-e468-48af-9ed0-c95cbba339ee] succeeded in 0.02018032300111372s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:41:52 WARNING celery.redirected 2026-01-05 04:41:52 INFO celery.app.trace Task connecthub.dispatcher.tick[14c2f4fa-f281-4e22-bc0f-0519084994d7] succeeded in 0.03138417900117929s: {'triggered': 1} +2026-01-05 04:41:52 INFO celery.app.trace Task connecthub.dispatcher.tick[14c2f4fa-f281-4e22-bc0f-0519084994d7] succeeded in 0.03138417900117929s: {'triggered': 1} +2026-01-05 04:41:52 WARNING celery.redirected 2026-01-05 04:41:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:41:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:41:52 WARNING celery.redirected 2026-01-05 04:41:52 INFO celery.app.trace Task connecthub.execute_job[eb26c574-0d81-4c37-a82d-e9a536e135de] succeeded in 0.05994634200033033s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:41:52 INFO celery.app.trace Task connecthub.execute_job[eb26c574-0d81-4c37-a82d-e9a536e135de] succeeded in 0.05994634200033033s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:42:52 WARNING celery.redirected 2026-01-05 04:42:52 INFO celery.app.trace Task connecthub.dispatcher.tick[ec860011-210c-41de-898c-bb70e0bbae26] succeeded in 0.012299804999202024s: {'triggered': 1} +2026-01-05 04:42:52 INFO celery.app.trace Task connecthub.dispatcher.tick[ec860011-210c-41de-898c-bb70e0bbae26] succeeded in 0.012299804999202024s: {'triggered': 1} +2026-01-05 04:42:52 WARNING celery.redirected 2026-01-05 04:42:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:42:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:42:52 WARNING celery.redirected 2026-01-05 04:42:52 INFO celery.app.trace Task connecthub.execute_job[8a13d019-2463-4473-97bd-c7c1fd57ecb3] succeeded in 0.01719682700058911s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:42:52 INFO celery.app.trace Task connecthub.execute_job[8a13d019-2463-4473-97bd-c7c1fd57ecb3] succeeded in 0.01719682700058911s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:43:52 WARNING celery.redirected 2026-01-05 04:43:52 INFO celery.app.trace Task connecthub.dispatcher.tick[46c26e9d-fe4e-4d14-98ff-0d535c4e7c5c] succeeded in 0.025048448000234202s: {'triggered': 1} +2026-01-05 04:43:52 INFO celery.app.trace Task connecthub.dispatcher.tick[46c26e9d-fe4e-4d14-98ff-0d535c4e7c5c] succeeded in 0.025048448000234202s: {'triggered': 1} +2026-01-05 04:43:52 WARNING celery.redirected 2026-01-05 04:43:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:43:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:43:52 WARNING celery.redirected 2026-01-05 04:43:52 INFO celery.app.trace Task connecthub.execute_job[da8a24ce-6102-401e-beb4-992577d5d295] succeeded in 0.025968660000216914s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:43:52 INFO celery.app.trace Task connecthub.execute_job[da8a24ce-6102-401e-beb4-992577d5d295] succeeded in 0.025968660000216914s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:44:31 WARNING celery.redirected 2026-01-05 04:44:31 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:44:31 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:44:31 WARNING celery.redirected 2026-01-05 04:44:31 INFO celery.app.trace Task connecthub.execute_job[dac4f0b6-f21a-4144-a8d0-414e4e2888a5] succeeded in 0.21572023400040052s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:44:31 INFO celery.app.trace Task connecthub.execute_job[dac4f0b6-f21a-4144-a8d0-414e4e2888a5] succeeded in 0.21572023400040052s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:45:12 WARNING celery.redirected 2026-01-05 04:45:12 INFO celery.app.trace Task connecthub.dispatcher.tick[de336f5c-6a6a-4e50-8dce-ed61ed6d1b84] succeeded in 0.01479602600011276s: {'triggered': 1} +2026-01-05 04:45:12 INFO celery.app.trace Task connecthub.dispatcher.tick[de336f5c-6a6a-4e50-8dce-ed61ed6d1b84] succeeded in 0.01479602600011276s: {'triggered': 1} +2026-01-05 04:45:12 WARNING celery.redirected 2026-01-05 04:45:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:45:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:45:12 WARNING celery.redirected 2026-01-05 04:45:12 INFO celery.app.trace Task connecthub.execute_job[b05503f6-84a2-4783-b99b-8d7d7d6dd9f4] succeeded in 0.07361554400085879s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:45:12 INFO celery.app.trace Task connecthub.execute_job[b05503f6-84a2-4783-b99b-8d7d7d6dd9f4] succeeded in 0.07361554400085879s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:46:12 WARNING celery.redirected 2026-01-05 04:46:12 INFO celery.app.trace Task connecthub.dispatcher.tick[d40edb78-19fd-4db5-bd8d-b90097e524c8] succeeded in 0.018784460000460967s: {'triggered': 1} +2026-01-05 04:46:12 INFO celery.app.trace Task connecthub.dispatcher.tick[d40edb78-19fd-4db5-bd8d-b90097e524c8] succeeded in 0.018784460000460967s: {'triggered': 1} +2026-01-05 04:46:12 WARNING celery.redirected 2026-01-05 04:46:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:46:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:46:12 WARNING celery.redirected 2026-01-05 04:46:12 INFO celery.app.trace Task connecthub.execute_job[be0b710b-093a-4682-9f0c-62aeb393d2c0] succeeded in 0.022114642000815365s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:46:12 INFO celery.app.trace Task connecthub.execute_job[be0b710b-093a-4682-9f0c-62aeb393d2c0] succeeded in 0.022114642000815365s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:46:57 WARNING celery.redirected 2026-01-05 04:46:57 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:46:57 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:46:57 WARNING celery.redirected 2026-01-05 04:46:57 INFO celery.app.trace Task connecthub.execute_job[78f34184-d547-4309-a95c-adc700459de3] succeeded in 0.048954084999422776s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:46:57 INFO celery.app.trace Task connecthub.execute_job[78f34184-d547-4309-a95c-adc700459de3] succeeded in 0.048954084999422776s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:47:12 WARNING celery.redirected 2026-01-05 04:47:12 INFO celery.app.trace Task connecthub.dispatcher.tick[ff0e6ec9-5e8f-40df-b640-ea07bff16dc8] succeeded in 0.011512289998790948s: {'triggered': 1} +2026-01-05 04:47:12 INFO celery.app.trace Task connecthub.dispatcher.tick[ff0e6ec9-5e8f-40df-b640-ea07bff16dc8] succeeded in 0.011512289998790948s: {'triggered': 1} +2026-01-05 04:47:12 WARNING celery.redirected 2026-01-05 04:47:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:47:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:47:12 WARNING celery.redirected 2026-01-05 04:47:12 INFO celery.app.trace Task connecthub.execute_job[77e78528-0624-4598-a396-df36683b5b4f] succeeded in 0.02458914800081402s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:47:12 INFO celery.app.trace Task connecthub.execute_job[77e78528-0624-4598-a396-df36683b5b4f] succeeded in 0.02458914800081402s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:48:12 WARNING celery.redirected 2026-01-05 04:48:12 INFO celery.app.trace Task connecthub.dispatcher.tick[f1815610-425b-4d28-a9b0-114018240569] succeeded in 0.016908452000279794s: {'triggered': 1} +2026-01-05 04:48:12 INFO celery.app.trace Task connecthub.dispatcher.tick[f1815610-425b-4d28-a9b0-114018240569] succeeded in 0.016908452000279794s: {'triggered': 1} +2026-01-05 04:48:12 WARNING celery.redirected 2026-01-05 04:48:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:48:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:48:12 WARNING celery.redirected 2026-01-05 04:48:12 INFO celery.app.trace Task connecthub.execute_job[4dd5a721-e52a-4d5e-8586-f6d4bfc813ae] succeeded in 0.02303668800050218s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:48:12 INFO celery.app.trace Task connecthub.execute_job[4dd5a721-e52a-4d5e-8586-f6d4bfc813ae] succeeded in 0.02303668800050218s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:49:12 WARNING celery.redirected 2026-01-05 04:49:12 INFO celery.app.trace Task connecthub.dispatcher.tick[fa284605-4e78-4406-b295-281a9e084930] succeeded in 0.012760109999362612s: {'triggered': 1} +2026-01-05 04:49:12 INFO celery.app.trace Task connecthub.dispatcher.tick[fa284605-4e78-4406-b295-281a9e084930] succeeded in 0.012760109999362612s: {'triggered': 1} +2026-01-05 04:49:12 WARNING celery.redirected 2026-01-05 04:49:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:49:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:49:12 WARNING celery.redirected 2026-01-05 04:49:12 INFO celery.app.trace Task connecthub.execute_job[48c6f2e3-2bbe-484e-aa45-117c46df7349] succeeded in 0.02080004699928395s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:49:12 INFO celery.app.trace Task connecthub.execute_job[48c6f2e3-2bbe-484e-aa45-117c46df7349] succeeded in 0.02080004699928395s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:50:12 WARNING celery.redirected 2026-01-05 04:50:12 INFO celery.app.trace Task connecthub.dispatcher.tick[c18e5a95-b652-41be-805f-1a456df578fc] succeeded in 0.010403339001641143s: {'triggered': 1} +2026-01-05 04:50:12 INFO celery.app.trace Task connecthub.dispatcher.tick[c18e5a95-b652-41be-805f-1a456df578fc] succeeded in 0.010403339001641143s: {'triggered': 1} +2026-01-05 04:50:12 WARNING celery.redirected 2026-01-05 04:50:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:50:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:50:12 WARNING celery.redirected 2026-01-05 04:50:12 INFO celery.app.trace Task connecthub.execute_job[69e7c7d2-8000-4578-8465-e53dbb1c8c57] succeeded in 0.015631820999260526s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:50:12 INFO celery.app.trace Task connecthub.execute_job[69e7c7d2-8000-4578-8465-e53dbb1c8c57] succeeded in 0.015631820999260526s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:51:12 WARNING celery.redirected 2026-01-05 04:51:12 INFO celery.app.trace Task connecthub.dispatcher.tick[4afd800b-22a4-499f-b28a-351da5d70495] succeeded in 0.025631652999436483s: {'triggered': 1} +2026-01-05 04:51:12 INFO celery.app.trace Task connecthub.dispatcher.tick[4afd800b-22a4-499f-b28a-351da5d70495] succeeded in 0.025631652999436483s: {'triggered': 1} +2026-01-05 04:51:12 WARNING celery.redirected 2026-01-05 04:51:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:51:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:51:12 WARNING celery.redirected 2026-01-05 04:51:12 INFO celery.app.trace Task connecthub.execute_job[24a44feb-d0d5-4dfd-b2ff-e96b856c3061] succeeded in 0.028806775000703055s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:51:12 INFO celery.app.trace Task connecthub.execute_job[24a44feb-d0d5-4dfd-b2ff-e96b856c3061] succeeded in 0.028806775000703055s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:52:12 WARNING celery.redirected 2026-01-05 04:52:12 INFO celery.app.trace Task connecthub.dispatcher.tick[b90582bc-17c7-40be-85b7-6d9bf01d900a] succeeded in 0.022004341999490862s: {'triggered': 1} +2026-01-05 04:52:12 INFO celery.app.trace Task connecthub.dispatcher.tick[b90582bc-17c7-40be-85b7-6d9bf01d900a] succeeded in 0.022004341999490862s: {'triggered': 1} +2026-01-05 04:52:12 WARNING celery.redirected 2026-01-05 04:52:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:52:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:52:12 WARNING celery.redirected 2026-01-05 04:52:12 INFO celery.app.trace Task connecthub.execute_job[229faba7-e293-4843-b30d-14e90ac574f6] succeeded in 0.02439292300005036s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:52:12 INFO celery.app.trace Task connecthub.execute_job[229faba7-e293-4843-b30d-14e90ac574f6] succeeded in 0.02439292300005036s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:53:12 WARNING celery.redirected 2026-01-05 04:53:12 INFO celery.app.trace Task connecthub.dispatcher.tick[2ea064eb-1eb1-46cd-9902-7a0965975e3c] succeeded in 0.014864144000966917s: {'triggered': 1} +2026-01-05 04:53:12 INFO celery.app.trace Task connecthub.dispatcher.tick[2ea064eb-1eb1-46cd-9902-7a0965975e3c] succeeded in 0.014864144000966917s: {'triggered': 1} +2026-01-05 04:53:12 WARNING celery.redirected 2026-01-05 04:53:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:53:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:53:12 WARNING celery.redirected 2026-01-05 04:53:12 INFO celery.app.trace Task connecthub.execute_job[8e2fe03f-f0fa-47e6-9ef5-cfa81c7391dc] succeeded in 0.02118985200104362s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:53:12 INFO celery.app.trace Task connecthub.execute_job[8e2fe03f-f0fa-47e6-9ef5-cfa81c7391dc] succeeded in 0.02118985200104362s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:54:12 WARNING celery.redirected 2026-01-05 04:54:12 INFO celery.app.trace Task connecthub.dispatcher.tick[2427c89d-2ed8-497a-81fd-9c87f5956671] succeeded in 0.018913083998995717s: {'triggered': 1} +2026-01-05 04:54:12 INFO celery.app.trace Task connecthub.dispatcher.tick[2427c89d-2ed8-497a-81fd-9c87f5956671] succeeded in 0.018913083998995717s: {'triggered': 1} +2026-01-05 04:54:12 WARNING celery.redirected 2026-01-05 04:54:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:54:12 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:54:12 WARNING celery.redirected 2026-01-05 04:54:12 INFO celery.app.trace Task connecthub.execute_job[14b00b85-efc7-4413-b613-4c65a15c11b8] succeeded in 0.023627791000762954s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:54:12 INFO celery.app.trace Task connecthub.execute_job[14b00b85-efc7-4413-b613-4c65a15c11b8] succeeded in 0.023627791000762954s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:55:46 WARNING celery.redirected 2026-01-05 04:55:46 INFO celery.app.trace Task connecthub.dispatcher.tick[fc8df83a-21bc-427e-b4bf-47680dbf65a4] succeeded in 0.051510687999325455s: {'triggered': 1} +2026-01-05 04:55:46 INFO celery.app.trace Task connecthub.dispatcher.tick[fc8df83a-21bc-427e-b4bf-47680dbf65a4] succeeded in 0.051510687999325455s: {'triggered': 1} +2026-01-05 04:55:46 WARNING celery.redirected 2026-01-05 04:55:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:55:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:55:46 WARNING celery.redirected 2026-01-05 04:55:46 INFO celery.app.trace Task connecthub.execute_job[a7845eff-bc4d-4d5c-9270-1c9dde5c94bc] succeeded in 0.08874253400063026s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:55:46 INFO celery.app.trace Task connecthub.execute_job[a7845eff-bc4d-4d5c-9270-1c9dde5c94bc] succeeded in 0.08874253400063026s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:56:46 WARNING celery.redirected 2026-01-05 04:56:46 INFO celery.app.trace Task connecthub.dispatcher.tick[81bf98fd-d3a4-42f0-903d-e774739f1760] succeeded in 0.029670317999261897s: {'triggered': 1} +2026-01-05 04:56:46 INFO celery.app.trace Task connecthub.dispatcher.tick[81bf98fd-d3a4-42f0-903d-e774739f1760] succeeded in 0.029670317999261897s: {'triggered': 1} +2026-01-05 04:56:46 WARNING celery.redirected 2026-01-05 04:56:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:56:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:56:46 WARNING celery.redirected 2026-01-05 04:56:46 INFO celery.app.trace Task connecthub.execute_job[01d6f1fc-edd5-4028-8a96-f095b3d3879a] succeeded in 0.024213059001340298s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:56:46 INFO celery.app.trace Task connecthub.execute_job[01d6f1fc-edd5-4028-8a96-f095b3d3879a] succeeded in 0.024213059001340298s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:57:46 WARNING celery.redirected 2026-01-05 04:57:46 INFO celery.app.trace Task connecthub.dispatcher.tick[b4d94d0e-d5cd-49eb-aa1f-aed065a3624a] succeeded in 0.013022064000324463s: {'triggered': 1} +2026-01-05 04:57:46 INFO celery.app.trace Task connecthub.dispatcher.tick[b4d94d0e-d5cd-49eb-aa1f-aed065a3624a] succeeded in 0.013022064000324463s: {'triggered': 1} +2026-01-05 04:57:46 WARNING celery.redirected 2026-01-05 04:57:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:57:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:57:46 WARNING celery.redirected 2026-01-05 04:57:46 INFO celery.app.trace Task connecthub.execute_job[588046a4-1f16-4a32-899d-43554a3a313a] succeeded in 0.02219619100105774s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:57:46 INFO celery.app.trace Task connecthub.execute_job[588046a4-1f16-4a32-899d-43554a3a313a] succeeded in 0.02219619100105774s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:58:46 WARNING celery.redirected 2026-01-05 04:58:46 INFO celery.app.trace Task connecthub.dispatcher.tick[fc89a00f-34cf-48ea-bfb1-f7e6988390ee] succeeded in 0.02372628299963253s: {'triggered': 1} +2026-01-05 04:58:46 INFO celery.app.trace Task connecthub.dispatcher.tick[fc89a00f-34cf-48ea-bfb1-f7e6988390ee] succeeded in 0.02372628299963253s: {'triggered': 1} +2026-01-05 04:58:46 WARNING celery.redirected 2026-01-05 04:58:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:58:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:58:46 WARNING celery.redirected 2026-01-05 04:58:46 INFO celery.app.trace Task connecthub.execute_job[9b693ff9-a561-48d4-ad17-3addcaf5b93e] succeeded in 0.02154406400040898s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:58:46 INFO celery.app.trace Task connecthub.execute_job[9b693ff9-a561-48d4-ad17-3addcaf5b93e] succeeded in 0.02154406400040898s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:59:46 WARNING celery.redirected 2026-01-05 04:59:46 INFO celery.app.trace Task connecthub.dispatcher.tick[dae1a177-3221-4737-9176-d8f930e8fa18] succeeded in 0.02139074299884669s: {'triggered': 1} +2026-01-05 04:59:46 INFO celery.app.trace Task connecthub.dispatcher.tick[dae1a177-3221-4737-9176-d8f930e8fa18] succeeded in 0.02139074299884669s: {'triggered': 1} +2026-01-05 04:59:46 WARNING celery.redirected 2026-01-05 04:59:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:59:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 04:59:46 WARNING celery.redirected 2026-01-05 04:59:46 INFO celery.app.trace Task connecthub.execute_job[9b9204ce-85e1-49f8-97bf-a4f8e6b278c7] succeeded in 0.02846042000055604s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 04:59:46 INFO celery.app.trace Task connecthub.execute_job[9b9204ce-85e1-49f8-97bf-a4f8e6b278c7] succeeded in 0.02846042000055604s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:00:46 WARNING celery.redirected 2026-01-05 05:00:46 INFO celery.app.trace Task connecthub.dispatcher.tick[da9246bf-9284-40d9-a9af-14732ee087ef] succeeded in 0.01989368699832994s: {'triggered': 1} +2026-01-05 05:00:46 INFO celery.app.trace Task connecthub.dispatcher.tick[da9246bf-9284-40d9-a9af-14732ee087ef] succeeded in 0.01989368699832994s: {'triggered': 1} +2026-01-05 05:00:46 WARNING celery.redirected 2026-01-05 05:00:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:00:46 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:00:46 WARNING celery.redirected 2026-01-05 05:00:46 INFO celery.app.trace Task connecthub.execute_job[36c8fdb0-3517-4013-9619-f76dc7407154] succeeded in 0.040899174000514904s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:00:46 INFO celery.app.trace Task connecthub.execute_job[36c8fdb0-3517-4013-9619-f76dc7407154] succeeded in 0.040899174000514904s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:45:31 WARNING celery.redirected 2026-01-05 05:45:31 INFO celery.app.trace Task connecthub.dispatcher.tick[27ca4879-110a-4585-b2fd-b98a107e1397] succeeded in 0.2113479939998797s: {'triggered': 1} +2026-01-05 05:45:31 INFO celery.app.trace Task connecthub.dispatcher.tick[27ca4879-110a-4585-b2fd-b98a107e1397] succeeded in 0.2113479939998797s: {'triggered': 1} +2026-01-05 05:45:31 WARNING celery.redirected 2026-01-05 05:45:31 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:45:31 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:45:31 WARNING celery.redirected 2026-01-05 05:45:31 INFO celery.app.trace Task connecthub.execute_job[672f034d-be35-44ab-bbb0-8d224a649312] succeeded in 0.454256250999606s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:45:31 INFO celery.app.trace Task connecthub.execute_job[672f034d-be35-44ab-bbb0-8d224a649312] succeeded in 0.454256250999606s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:46:30 WARNING celery.redirected 2026-01-05 05:46:30 INFO celery.app.trace Task connecthub.dispatcher.tick[d620ca1e-6b90-4c80-80e6-1e6ac71c1572] succeeded in 0.03550281900061236s: {'triggered': 1} +2026-01-05 05:46:30 INFO celery.app.trace Task connecthub.dispatcher.tick[d620ca1e-6b90-4c80-80e6-1e6ac71c1572] succeeded in 0.03550281900061236s: {'triggered': 1} +2026-01-05 05:46:30 WARNING celery.redirected 2026-01-05 05:46:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:46:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:46:30 WARNING celery.redirected 2026-01-05 05:46:30 INFO celery.app.trace Task connecthub.execute_job[58573d2a-c361-4505-8162-9000993ab39e] succeeded in 0.032898728000873234s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:46:30 INFO celery.app.trace Task connecthub.execute_job[58573d2a-c361-4505-8162-9000993ab39e] succeeded in 0.032898728000873234s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:47:30 WARNING celery.redirected 2026-01-05 05:47:30 INFO celery.app.trace Task connecthub.dispatcher.tick[e428d868-3685-4bdd-8ffd-a9904da8ea08] succeeded in 0.016852472001119168s: {'triggered': 1} +2026-01-05 05:47:30 INFO celery.app.trace Task connecthub.dispatcher.tick[e428d868-3685-4bdd-8ffd-a9904da8ea08] succeeded in 0.016852472001119168s: {'triggered': 1} +2026-01-05 05:47:30 WARNING celery.redirected 2026-01-05 05:47:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:47:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:47:30 WARNING celery.redirected 2026-01-05 05:47:30 INFO celery.app.trace Task connecthub.execute_job[55f28990-97d2-4d7f-ba26-6fefe7e3dd5f] succeeded in 0.023192691000076593s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:47:30 INFO celery.app.trace Task connecthub.execute_job[55f28990-97d2-4d7f-ba26-6fefe7e3dd5f] succeeded in 0.023192691000076593s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:48:30 WARNING celery.redirected 2026-01-05 05:48:30 INFO celery.app.trace Task connecthub.dispatcher.tick[f81f7d17-5af4-46e6-8c3f-50d87bdbb342] succeeded in 0.020918959000482573s: {'triggered': 1} +2026-01-05 05:48:30 INFO celery.app.trace Task connecthub.dispatcher.tick[f81f7d17-5af4-46e6-8c3f-50d87bdbb342] succeeded in 0.020918959000482573s: {'triggered': 1} +2026-01-05 05:48:30 WARNING celery.redirected 2026-01-05 05:48:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:48:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:48:30 WARNING celery.redirected 2026-01-05 05:48:30 INFO celery.app.trace Task connecthub.execute_job[9a94f156-60ee-49ed-9014-e8edd886ba54] succeeded in 0.020251587999155163s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:48:30 INFO celery.app.trace Task connecthub.execute_job[9a94f156-60ee-49ed-9014-e8edd886ba54] succeeded in 0.020251587999155163s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:49:30 WARNING celery.redirected 2026-01-05 05:49:30 INFO celery.app.trace Task connecthub.dispatcher.tick[91b53351-4145-4eb0-98ee-fb8e03bc49ee] succeeded in 0.024995726998895407s: {'triggered': 1} +2026-01-05 05:49:30 INFO celery.app.trace Task connecthub.dispatcher.tick[91b53351-4145-4eb0-98ee-fb8e03bc49ee] succeeded in 0.024995726998895407s: {'triggered': 1} +2026-01-05 05:49:30 WARNING celery.redirected 2026-01-05 05:49:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:49:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:49:30 WARNING celery.redirected 2026-01-05 05:49:30 INFO celery.app.trace Task connecthub.execute_job[3b75b748-2eef-4c51-a45b-7e6b5f33c83a] succeeded in 0.03371988400067494s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:49:30 INFO celery.app.trace Task connecthub.execute_job[3b75b748-2eef-4c51-a45b-7e6b5f33c83a] succeeded in 0.03371988400067494s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:50:30 WARNING celery.redirected 2026-01-05 05:50:30 INFO celery.app.trace Task connecthub.dispatcher.tick[26a47a17-9e8d-4de0-8ed4-ad44a69b20bc] succeeded in 0.029451242000504863s: {'triggered': 1} +2026-01-05 05:50:30 INFO celery.app.trace Task connecthub.dispatcher.tick[26a47a17-9e8d-4de0-8ed4-ad44a69b20bc] succeeded in 0.029451242000504863s: {'triggered': 1} +2026-01-05 05:50:30 WARNING celery.redirected 2026-01-05 05:50:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:50:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:50:30 WARNING celery.redirected 2026-01-05 05:50:30 INFO celery.app.trace Task connecthub.execute_job[bcaad853-aa66-4766-aa54-eef8b1a009a1] succeeded in 0.03852558600010525s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:50:30 INFO celery.app.trace Task connecthub.execute_job[bcaad853-aa66-4766-aa54-eef8b1a009a1] succeeded in 0.03852558600010525s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:51:30 WARNING celery.redirected 2026-01-05 05:51:30 INFO celery.app.trace Task connecthub.dispatcher.tick[d3d455ab-d101-42d5-af05-f3fcc45a747c] succeeded in 0.02421432000119239s: {'triggered': 1} +2026-01-05 05:51:30 INFO celery.app.trace Task connecthub.dispatcher.tick[d3d455ab-d101-42d5-af05-f3fcc45a747c] succeeded in 0.02421432000119239s: {'triggered': 1} +2026-01-05 05:51:30 WARNING celery.redirected 2026-01-05 05:51:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:51:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:51:30 WARNING celery.redirected 2026-01-05 05:51:30 INFO celery.app.trace Task connecthub.execute_job[a151e738-563b-4f35-b402-5008d5c0cfa8] succeeded in 0.0239177359999303s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:51:30 INFO celery.app.trace Task connecthub.execute_job[a151e738-563b-4f35-b402-5008d5c0cfa8] succeeded in 0.0239177359999303s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:52:30 WARNING celery.redirected 2026-01-05 05:52:30 INFO celery.app.trace Task connecthub.dispatcher.tick[4df3e07b-634a-4781-ad7a-2e72556d90e8] succeeded in 0.011886543001310201s: {'triggered': 1} +2026-01-05 05:52:30 INFO celery.app.trace Task connecthub.dispatcher.tick[4df3e07b-634a-4781-ad7a-2e72556d90e8] succeeded in 0.011886543001310201s: {'triggered': 1} +2026-01-05 05:52:30 WARNING celery.redirected 2026-01-05 05:52:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:52:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:52:30 WARNING celery.redirected 2026-01-05 05:52:30 INFO celery.app.trace Task connecthub.execute_job[6bc8805f-ec48-4271-87ef-9595169953ca] succeeded in 0.025019463999342406s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:52:30 INFO celery.app.trace Task connecthub.execute_job[6bc8805f-ec48-4271-87ef-9595169953ca] succeeded in 0.025019463999342406s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:53:30 WARNING celery.redirected 2026-01-05 05:53:30 INFO celery.app.trace Task connecthub.dispatcher.tick[c01a1481-3a8d-4dde-8e87-654fbd6089bd] succeeded in 0.011546083000212093s: {'triggered': 1} +2026-01-05 05:53:30 INFO celery.app.trace Task connecthub.dispatcher.tick[c01a1481-3a8d-4dde-8e87-654fbd6089bd] succeeded in 0.011546083000212093s: {'triggered': 1} +2026-01-05 05:53:30 WARNING celery.redirected 2026-01-05 05:53:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:53:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:53:30 WARNING celery.redirected 2026-01-05 05:53:30 INFO celery.app.trace Task connecthub.execute_job[349a430d-9cfd-46d4-8ed2-6d2399c5ed03] succeeded in 0.02823889299907023s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:53:30 INFO celery.app.trace Task connecthub.execute_job[349a430d-9cfd-46d4-8ed2-6d2399c5ed03] succeeded in 0.02823889299907023s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:54:30 WARNING celery.redirected 2026-01-05 05:54:30 INFO celery.app.trace Task connecthub.dispatcher.tick[ab81f78d-088c-43f3-beba-78b2482d225a] succeeded in 0.017373353999573737s: {'triggered': 1} +2026-01-05 05:54:30 INFO celery.app.trace Task connecthub.dispatcher.tick[ab81f78d-088c-43f3-beba-78b2482d225a] succeeded in 0.017373353999573737s: {'triggered': 1} +2026-01-05 05:54:30 WARNING celery.redirected 2026-01-05 05:54:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:54:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:54:30 WARNING celery.redirected 2026-01-05 05:54:30 INFO celery.app.trace Task connecthub.execute_job[d7069a63-717f-4133-8637-e2757b642ffd] succeeded in 0.03503616700072598s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:54:30 INFO celery.app.trace Task connecthub.execute_job[d7069a63-717f-4133-8637-e2757b642ffd] succeeded in 0.03503616700072598s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:55:30 WARNING celery.redirected 2026-01-05 05:55:30 INFO celery.app.trace Task connecthub.dispatcher.tick[94abcaec-3182-46bc-a062-de6bede82847] succeeded in 0.013367278999794507s: {'triggered': 1} +2026-01-05 05:55:30 INFO celery.app.trace Task connecthub.dispatcher.tick[94abcaec-3182-46bc-a062-de6bede82847] succeeded in 0.013367278999794507s: {'triggered': 1} +2026-01-05 05:55:30 WARNING celery.redirected 2026-01-05 05:55:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:55:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:55:30 WARNING celery.redirected 2026-01-05 05:55:30 INFO celery.app.trace Task connecthub.execute_job[2a9633a4-b4e5-4127-a27c-c31c9a65ec4d] succeeded in 0.017370081000990467s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:55:30 INFO celery.app.trace Task connecthub.execute_job[2a9633a4-b4e5-4127-a27c-c31c9a65ec4d] succeeded in 0.017370081000990467s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:56:30 WARNING celery.redirected 2026-01-05 05:56:30 INFO celery.app.trace Task connecthub.dispatcher.tick[3b23f948-6593-46af-b3e9-ba71cef0f94f] succeeded in 0.012565402999825892s: {'triggered': 1} +2026-01-05 05:56:30 INFO celery.app.trace Task connecthub.dispatcher.tick[3b23f948-6593-46af-b3e9-ba71cef0f94f] succeeded in 0.012565402999825892s: {'triggered': 1} +2026-01-05 05:56:30 WARNING celery.redirected 2026-01-05 05:56:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:56:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:56:30 WARNING celery.redirected 2026-01-05 05:56:30 INFO celery.app.trace Task connecthub.execute_job[e487ce16-e7a6-4595-a8ad-5561957fb9b0] succeeded in 0.03014952500052459s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:56:30 INFO celery.app.trace Task connecthub.execute_job[e487ce16-e7a6-4595-a8ad-5561957fb9b0] succeeded in 0.03014952500052459s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:57:30 WARNING celery.redirected 2026-01-05 05:57:30 INFO celery.app.trace Task connecthub.dispatcher.tick[209e4885-328b-4b37-ada6-2724dc937bff] succeeded in 0.0279219309995824s: {'triggered': 1} +2026-01-05 05:57:30 INFO celery.app.trace Task connecthub.dispatcher.tick[209e4885-328b-4b37-ada6-2724dc937bff] succeeded in 0.0279219309995824s: {'triggered': 1} +2026-01-05 05:57:30 WARNING celery.redirected 2026-01-05 05:57:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:57:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:57:30 WARNING celery.redirected 2026-01-05 05:57:30 INFO celery.app.trace Task connecthub.execute_job[c3b24e32-e1c6-401d-8a0c-c723a5f4bb2d] succeeded in 0.039173844999822904s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:57:30 INFO celery.app.trace Task connecthub.execute_job[c3b24e32-e1c6-401d-8a0c-c723a5f4bb2d] succeeded in 0.039173844999822904s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:58:30 WARNING celery.redirected 2026-01-05 05:58:30 INFO celery.app.trace Task connecthub.dispatcher.tick[2be340dd-2654-4ac0-96e5-365d25616cb5] succeeded in 0.04025455699957092s: {'triggered': 1} +2026-01-05 05:58:30 INFO celery.app.trace Task connecthub.dispatcher.tick[2be340dd-2654-4ac0-96e5-365d25616cb5] succeeded in 0.04025455699957092s: {'triggered': 1} +2026-01-05 05:58:30 WARNING celery.redirected 2026-01-05 05:58:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:58:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:58:30 WARNING celery.redirected 2026-01-05 05:58:30 INFO celery.app.trace Task connecthub.execute_job[1f61f517-1bd5-4a8c-af48-afcf593f4aab] succeeded in 0.027227636999668903s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:58:30 INFO celery.app.trace Task connecthub.execute_job[1f61f517-1bd5-4a8c-af48-afcf593f4aab] succeeded in 0.027227636999668903s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:59:30 WARNING celery.redirected 2026-01-05 05:59:30 INFO celery.app.trace Task connecthub.dispatcher.tick[45a8cccb-f484-45d5-8014-96d540d5b4c1] succeeded in 0.008572890999857918s: {'triggered': 1} +2026-01-05 05:59:30 INFO celery.app.trace Task connecthub.dispatcher.tick[45a8cccb-f484-45d5-8014-96d540d5b4c1] succeeded in 0.008572890999857918s: {'triggered': 1} +2026-01-05 05:59:30 WARNING celery.redirected 2026-01-05 05:59:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:59:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 05:59:30 WARNING celery.redirected 2026-01-05 05:59:30 INFO celery.app.trace Task connecthub.execute_job[5097f1f5-888e-4c64-8af1-46f3a7d8a5d7] succeeded in 0.016939488999923924s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 05:59:30 INFO celery.app.trace Task connecthub.execute_job[5097f1f5-888e-4c64-8af1-46f3a7d8a5d7] succeeded in 0.016939488999923924s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:00:30 WARNING celery.redirected 2026-01-05 06:00:30 INFO celery.app.trace Task connecthub.dispatcher.tick[8069c5e7-04aa-4c8f-846e-fb7c127747a0] succeeded in 0.012104123999961303s: {'triggered': 1} +2026-01-05 06:00:30 INFO celery.app.trace Task connecthub.dispatcher.tick[8069c5e7-04aa-4c8f-846e-fb7c127747a0] succeeded in 0.012104123999961303s: {'triggered': 1} +2026-01-05 06:00:30 WARNING celery.redirected 2026-01-05 06:00:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:00:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:00:30 WARNING celery.redirected 2026-01-05 06:00:30 INFO celery.app.trace Task connecthub.execute_job[49652422-5d0a-4615-98f1-74d3a0dc807d] succeeded in 0.02802831700137176s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:00:30 INFO celery.app.trace Task connecthub.execute_job[49652422-5d0a-4615-98f1-74d3a0dc807d] succeeded in 0.02802831700137176s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:01:30 WARNING celery.redirected 2026-01-05 06:01:30 INFO celery.app.trace Task connecthub.dispatcher.tick[4df18747-7216-43ae-a4f6-77a8c8eba94d] succeeded in 0.03370886000084283s: {'triggered': 1} +2026-01-05 06:01:30 INFO celery.app.trace Task connecthub.dispatcher.tick[4df18747-7216-43ae-a4f6-77a8c8eba94d] succeeded in 0.03370886000084283s: {'triggered': 1} +2026-01-05 06:01:30 WARNING celery.redirected 2026-01-05 06:01:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:01:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:01:30 WARNING celery.redirected 2026-01-05 06:01:30 INFO celery.app.trace Task connecthub.execute_job[33bd16cd-69b1-4892-a85b-2612e55425aa] succeeded in 0.0320245969996904s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:01:30 INFO celery.app.trace Task connecthub.execute_job[33bd16cd-69b1-4892-a85b-2612e55425aa] succeeded in 0.0320245969996904s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:02:30 WARNING celery.redirected 2026-01-05 06:02:30 INFO celery.app.trace Task connecthub.dispatcher.tick[86fbee18-65b8-477a-9d3c-3826b1e5ee80] succeeded in 0.017320659999313648s: {'triggered': 1} +2026-01-05 06:02:30 INFO celery.app.trace Task connecthub.dispatcher.tick[86fbee18-65b8-477a-9d3c-3826b1e5ee80] succeeded in 0.017320659999313648s: {'triggered': 1} +2026-01-05 06:02:30 WARNING celery.redirected 2026-01-05 06:02:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:02:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:02:30 WARNING celery.redirected 2026-01-05 06:02:30 INFO celery.app.trace Task connecthub.execute_job[83552280-2a96-4fd9-a0be-99ca93901ee7] succeeded in 0.028892322998217423s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:02:30 INFO celery.app.trace Task connecthub.execute_job[83552280-2a96-4fd9-a0be-99ca93901ee7] succeeded in 0.028892322998217423s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:03:30 WARNING celery.redirected 2026-01-05 06:03:30 INFO celery.app.trace Task connecthub.dispatcher.tick[cfd8a733-90f0-4d3c-b720-2d41df04404d] succeeded in 0.017844455000158632s: {'triggered': 1} +2026-01-05 06:03:30 INFO celery.app.trace Task connecthub.dispatcher.tick[cfd8a733-90f0-4d3c-b720-2d41df04404d] succeeded in 0.017844455000158632s: {'triggered': 1} +2026-01-05 06:03:30 WARNING celery.redirected 2026-01-05 06:03:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:03:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:03:30 WARNING celery.redirected 2026-01-05 06:03:30 INFO celery.app.trace Task connecthub.execute_job[49ceefed-0b5b-4899-8759-e76dc5c74e3a] succeeded in 0.024473126000884804s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:03:30 INFO celery.app.trace Task connecthub.execute_job[49ceefed-0b5b-4899-8759-e76dc5c74e3a] succeeded in 0.024473126000884804s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:04:30 WARNING celery.redirected 2026-01-05 06:04:30 INFO celery.app.trace Task connecthub.dispatcher.tick[d373478b-45e9-4000-bffd-24b0de6bb935] succeeded in 0.016729655999370152s: {'triggered': 1} +2026-01-05 06:04:30 INFO celery.app.trace Task connecthub.dispatcher.tick[d373478b-45e9-4000-bffd-24b0de6bb935] succeeded in 0.016729655999370152s: {'triggered': 1} +2026-01-05 06:04:30 WARNING celery.redirected 2026-01-05 06:04:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:04:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:04:30 WARNING celery.redirected 2026-01-05 06:04:30 INFO celery.app.trace Task connecthub.execute_job[a71695a5-b9ee-4f1b-bd19-d831285aaa51] succeeded in 0.021148603000256117s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:04:30 INFO celery.app.trace Task connecthub.execute_job[a71695a5-b9ee-4f1b-bd19-d831285aaa51] succeeded in 0.021148603000256117s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:05:30 WARNING celery.redirected 2026-01-05 06:05:30 INFO celery.app.trace Task connecthub.dispatcher.tick[b9f4be59-62cc-41eb-bc94-054732dce1a4] succeeded in 0.014041430000361288s: {'triggered': 1} +2026-01-05 06:05:30 INFO celery.app.trace Task connecthub.dispatcher.tick[b9f4be59-62cc-41eb-bc94-054732dce1a4] succeeded in 0.014041430000361288s: {'triggered': 1} +2026-01-05 06:05:30 WARNING celery.redirected 2026-01-05 06:05:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:05:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:05:30 WARNING celery.redirected 2026-01-05 06:05:30 INFO celery.app.trace Task connecthub.execute_job[35188251-969c-4e41-96bf-9288ef52a973] succeeded in 0.020802560000447556s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:05:30 INFO celery.app.trace Task connecthub.execute_job[35188251-969c-4e41-96bf-9288ef52a973] succeeded in 0.020802560000447556s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:06:30 WARNING celery.redirected 2026-01-05 06:06:30 INFO celery.app.trace Task connecthub.dispatcher.tick[3c80a955-cc8c-4c39-b6e4-21ce4ac1b850] succeeded in 0.010666323001714773s: {'triggered': 1} +2026-01-05 06:06:30 INFO celery.app.trace Task connecthub.dispatcher.tick[3c80a955-cc8c-4c39-b6e4-21ce4ac1b850] succeeded in 0.010666323001714773s: {'triggered': 1} +2026-01-05 06:06:30 WARNING celery.redirected 2026-01-05 06:06:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:06:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:06:30 WARNING celery.redirected 2026-01-05 06:06:30 INFO celery.app.trace Task connecthub.execute_job[4032d14b-e2f9-4ffe-b038-b6de169c8c75] succeeded in 0.022911324000233435s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:06:30 INFO celery.app.trace Task connecthub.execute_job[4032d14b-e2f9-4ffe-b038-b6de169c8c75] succeeded in 0.022911324000233435s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:07:30 WARNING celery.redirected 2026-01-05 06:07:30 INFO celery.app.trace Task connecthub.dispatcher.tick[4e2bd2c6-b54f-41f8-af47-4a77bae7a287] succeeded in 0.042610238000634126s: {'triggered': 1} +2026-01-05 06:07:30 INFO celery.app.trace Task connecthub.dispatcher.tick[4e2bd2c6-b54f-41f8-af47-4a77bae7a287] succeeded in 0.042610238000634126s: {'triggered': 1} +2026-01-05 06:07:30 WARNING celery.redirected 2026-01-05 06:07:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:07:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:07:30 WARNING celery.redirected 2026-01-05 06:07:30 INFO celery.app.trace Task connecthub.execute_job[259a0a3d-55f4-48ff-ac97-65489829d694] succeeded in 0.05615256400051294s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:07:30 INFO celery.app.trace Task connecthub.execute_job[259a0a3d-55f4-48ff-ac97-65489829d694] succeeded in 0.05615256400051294s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:08:30 WARNING celery.redirected 2026-01-05 06:08:30 INFO celery.app.trace Task connecthub.dispatcher.tick[57f37a04-8e64-4700-a20f-9877151550d1] succeeded in 0.03720585700102674s: {'triggered': 1} +2026-01-05 06:08:30 INFO celery.app.trace Task connecthub.dispatcher.tick[57f37a04-8e64-4700-a20f-9877151550d1] succeeded in 0.03720585700102674s: {'triggered': 1} +2026-01-05 06:08:30 WARNING celery.redirected 2026-01-05 06:08:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:08:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:08:30 WARNING celery.redirected 2026-01-05 06:08:30 INFO celery.app.trace Task connecthub.execute_job[5b646856-176a-4096-99ac-491f0322a391] succeeded in 0.05866147000051569s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:08:30 INFO celery.app.trace Task connecthub.execute_job[5b646856-176a-4096-99ac-491f0322a391] succeeded in 0.05866147000051569s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:09:30 WARNING celery.redirected 2026-01-05 06:09:30 INFO celery.app.trace Task connecthub.dispatcher.tick[4af79bdd-f846-43f4-a35c-9649d1ab6820] succeeded in 0.012601733998963027s: {'triggered': 1} +2026-01-05 06:09:30 INFO celery.app.trace Task connecthub.dispatcher.tick[4af79bdd-f846-43f4-a35c-9649d1ab6820] succeeded in 0.012601733998963027s: {'triggered': 1} +2026-01-05 06:09:30 WARNING celery.redirected 2026-01-05 06:09:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:09:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:09:30 WARNING celery.redirected 2026-01-05 06:09:30 INFO celery.app.trace Task connecthub.execute_job[15a764cc-58f2-4ad5-9a1b-bb106d10d853] succeeded in 0.02483064200168883s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:09:30 INFO celery.app.trace Task connecthub.execute_job[15a764cc-58f2-4ad5-9a1b-bb106d10d853] succeeded in 0.02483064200168883s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:10:30 WARNING celery.redirected 2026-01-05 06:10:30 INFO celery.app.trace Task connecthub.dispatcher.tick[4c821dc4-354d-4b27-9d10-fd0584b411c3] succeeded in 0.025750164999408298s: {'triggered': 1} +2026-01-05 06:10:30 INFO celery.app.trace Task connecthub.dispatcher.tick[4c821dc4-354d-4b27-9d10-fd0584b411c3] succeeded in 0.025750164999408298s: {'triggered': 1} +2026-01-05 06:10:30 WARNING celery.redirected 2026-01-05 06:10:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:10:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:10:30 WARNING celery.redirected 2026-01-05 06:10:30 INFO celery.app.trace Task connecthub.execute_job[64938c6b-c50b-462b-a319-e64853d7e738] succeeded in 0.024463608999212738s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:10:30 INFO celery.app.trace Task connecthub.execute_job[64938c6b-c50b-462b-a319-e64853d7e738] succeeded in 0.024463608999212738s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:11:30 WARNING celery.redirected 2026-01-05 06:11:30 INFO celery.app.trace Task connecthub.dispatcher.tick[8f8dfad2-c64f-4cf6-9204-700da8d460d5] succeeded in 0.01863696100008383s: {'triggered': 1} +2026-01-05 06:11:30 INFO celery.app.trace Task connecthub.dispatcher.tick[8f8dfad2-c64f-4cf6-9204-700da8d460d5] succeeded in 0.01863696100008383s: {'triggered': 1} +2026-01-05 06:11:30 WARNING celery.redirected 2026-01-05 06:11:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:11:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:11:30 WARNING celery.redirected 2026-01-05 06:11:30 INFO celery.app.trace Task connecthub.execute_job[0e58c0e7-722a-4fc2-b95c-688e3fc1a253] succeeded in 0.025985803000367014s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:11:30 INFO celery.app.trace Task connecthub.execute_job[0e58c0e7-722a-4fc2-b95c-688e3fc1a253] succeeded in 0.025985803000367014s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:12:30 WARNING celery.redirected 2026-01-05 06:12:30 INFO celery.app.trace Task connecthub.dispatcher.tick[36368e0e-7ff7-4de5-9721-067f54671c68] succeeded in 0.017128098999819485s: {'triggered': 1} +2026-01-05 06:12:30 INFO celery.app.trace Task connecthub.dispatcher.tick[36368e0e-7ff7-4de5-9721-067f54671c68] succeeded in 0.017128098999819485s: {'triggered': 1} +2026-01-05 06:12:30 WARNING celery.redirected 2026-01-05 06:12:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:12:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:12:30 WARNING celery.redirected 2026-01-05 06:12:30 INFO celery.app.trace Task connecthub.execute_job[7d7297e3-7970-44e4-857e-2c805f69070f] succeeded in 0.024267677999887383s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:12:30 INFO celery.app.trace Task connecthub.execute_job[7d7297e3-7970-44e4-857e-2c805f69070f] succeeded in 0.024267677999887383s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:13:30 WARNING celery.redirected 2026-01-05 06:13:30 INFO celery.app.trace Task connecthub.dispatcher.tick[346a0cda-08cd-4885-b91b-8ff867751d77] succeeded in 0.03172679700037406s: {'triggered': 1} +2026-01-05 06:13:30 INFO celery.app.trace Task connecthub.dispatcher.tick[346a0cda-08cd-4885-b91b-8ff867751d77] succeeded in 0.03172679700037406s: {'triggered': 1} +2026-01-05 06:13:30 WARNING celery.redirected 2026-01-05 06:13:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:13:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:13:30 WARNING celery.redirected 2026-01-05 06:13:30 INFO celery.app.trace Task connecthub.execute_job[5899afc1-2ce0-48a1-93af-6c84715551ac] succeeded in 0.027441657999588642s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:13:30 INFO celery.app.trace Task connecthub.execute_job[5899afc1-2ce0-48a1-93af-6c84715551ac] succeeded in 0.027441657999588642s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:14:30 WARNING celery.redirected 2026-01-05 06:14:30 INFO celery.app.trace Task connecthub.dispatcher.tick[bbee957f-6a26-4b47-8506-51bf24ff9a97] succeeded in 0.02345835599953716s: {'triggered': 1} +2026-01-05 06:14:30 INFO celery.app.trace Task connecthub.dispatcher.tick[bbee957f-6a26-4b47-8506-51bf24ff9a97] succeeded in 0.02345835599953716s: {'triggered': 1} +2026-01-05 06:14:30 WARNING celery.redirected 2026-01-05 06:14:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:14:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:14:30 WARNING celery.redirected 2026-01-05 06:14:30 INFO celery.app.trace Task connecthub.execute_job[3757768b-5f7f-454a-847d-d063e2658849] succeeded in 0.04374712600110797s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:14:30 INFO celery.app.trace Task connecthub.execute_job[3757768b-5f7f-454a-847d-d063e2658849] succeeded in 0.04374712600110797s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:15:30 WARNING celery.redirected 2026-01-05 06:15:30 INFO celery.app.trace Task connecthub.dispatcher.tick[2dfbd768-2aae-449f-9b7d-63928813bc10] succeeded in 0.02275668800029962s: {'triggered': 1} +2026-01-05 06:15:30 INFO celery.app.trace Task connecthub.dispatcher.tick[2dfbd768-2aae-449f-9b7d-63928813bc10] succeeded in 0.02275668800029962s: {'triggered': 1} +2026-01-05 06:15:30 WARNING celery.redirected 2026-01-05 06:15:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:15:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:15:30 WARNING celery.redirected 2026-01-05 06:15:30 INFO celery.app.trace Task connecthub.execute_job[59870fc5-d68a-425f-a1fc-3af1e352bc3b] succeeded in 0.025605107999581378s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:15:30 INFO celery.app.trace Task connecthub.execute_job[59870fc5-d68a-425f-a1fc-3af1e352bc3b] succeeded in 0.025605107999581378s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:16:30 WARNING celery.redirected 2026-01-05 06:16:30 INFO celery.app.trace Task connecthub.dispatcher.tick[f3fcb441-af6d-4811-b683-83578f65d06a] succeeded in 0.02731711600063136s: {'triggered': 1} +2026-01-05 06:16:30 INFO celery.app.trace Task connecthub.dispatcher.tick[f3fcb441-af6d-4811-b683-83578f65d06a] succeeded in 0.02731711600063136s: {'triggered': 1} +2026-01-05 06:16:30 WARNING celery.redirected 2026-01-05 06:16:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:16:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:16:30 WARNING celery.redirected 2026-01-05 06:16:30 INFO celery.app.trace Task connecthub.execute_job[8171cb15-18e1-4683-90d9-792953bc79ae] succeeded in 0.032392187998993904s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:16:30 INFO celery.app.trace Task connecthub.execute_job[8171cb15-18e1-4683-90d9-792953bc79ae] succeeded in 0.032392187998993904s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:17:30 WARNING celery.redirected 2026-01-05 06:17:30 INFO celery.app.trace Task connecthub.dispatcher.tick[873a3f98-382b-4f55-8228-d14b53eaa27d] succeeded in 0.01927227899977879s: {'triggered': 1} +2026-01-05 06:17:30 INFO celery.app.trace Task connecthub.dispatcher.tick[873a3f98-382b-4f55-8228-d14b53eaa27d] succeeded in 0.01927227899977879s: {'triggered': 1} +2026-01-05 06:17:30 WARNING celery.redirected 2026-01-05 06:17:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:17:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:17:30 WARNING celery.redirected 2026-01-05 06:17:30 INFO celery.app.trace Task connecthub.execute_job[6f1b63c9-7733-4215-8963-4898bad700d9] succeeded in 0.03501303600023675s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:17:30 INFO celery.app.trace Task connecthub.execute_job[6f1b63c9-7733-4215-8963-4898bad700d9] succeeded in 0.03501303600023675s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:18:30 WARNING celery.redirected 2026-01-05 06:18:30 INFO celery.app.trace Task connecthub.dispatcher.tick[ab8a55b4-a1f8-42fc-abf5-5aaf2e0e8eee] succeeded in 0.0179667430002155s: {'triggered': 1} +2026-01-05 06:18:30 INFO celery.app.trace Task connecthub.dispatcher.tick[ab8a55b4-a1f8-42fc-abf5-5aaf2e0e8eee] succeeded in 0.0179667430002155s: {'triggered': 1} +2026-01-05 06:18:30 WARNING celery.redirected 2026-01-05 06:18:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:18:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:18:30 WARNING celery.redirected 2026-01-05 06:18:30 INFO celery.app.trace Task connecthub.execute_job[c1c3b21c-eae1-4f19-b1b1-f4fc51556a79] succeeded in 0.02200347799953306s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:18:30 INFO celery.app.trace Task connecthub.execute_job[c1c3b21c-eae1-4f19-b1b1-f4fc51556a79] succeeded in 0.02200347799953306s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:19:30 WARNING celery.redirected 2026-01-05 06:19:30 INFO celery.app.trace Task connecthub.dispatcher.tick[429c77cd-0eb2-432f-8fe7-f3f192338bfe] succeeded in 0.0328237989997433s: {'triggered': 1} +2026-01-05 06:19:30 INFO celery.app.trace Task connecthub.dispatcher.tick[429c77cd-0eb2-432f-8fe7-f3f192338bfe] succeeded in 0.0328237989997433s: {'triggered': 1} +2026-01-05 06:19:30 WARNING celery.redirected 2026-01-05 06:19:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:19:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:19:30 WARNING celery.redirected 2026-01-05 06:19:30 INFO celery.app.trace Task connecthub.execute_job[d110f126-af61-4622-899c-e2c857eb40f7] succeeded in 0.02007196599879535s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:19:30 INFO celery.app.trace Task connecthub.execute_job[d110f126-af61-4622-899c-e2c857eb40f7] succeeded in 0.02007196599879535s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:20:30 WARNING celery.redirected 2026-01-05 06:20:30 INFO celery.app.trace Task connecthub.dispatcher.tick[90eae2e2-05a2-45fe-a9ed-1c563f426224] succeeded in 0.012721692000923213s: {'triggered': 1} +2026-01-05 06:20:30 INFO celery.app.trace Task connecthub.dispatcher.tick[90eae2e2-05a2-45fe-a9ed-1c563f426224] succeeded in 0.012721692000923213s: {'triggered': 1} +2026-01-05 06:20:30 WARNING celery.redirected 2026-01-05 06:20:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:20:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:20:30 WARNING celery.redirected 2026-01-05 06:20:30 INFO celery.app.trace Task connecthub.execute_job[5b8790da-2912-4973-b72e-819a9023aef2] succeeded in 0.02931942000031995s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:20:30 INFO celery.app.trace Task connecthub.execute_job[5b8790da-2912-4973-b72e-819a9023aef2] succeeded in 0.02931942000031995s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:21:30 WARNING celery.redirected 2026-01-05 06:21:30 INFO celery.app.trace Task connecthub.dispatcher.tick[440c4f3d-5775-4315-95d8-6628fc419117] succeeded in 0.023760617999869282s: {'triggered': 1} +2026-01-05 06:21:30 INFO celery.app.trace Task connecthub.dispatcher.tick[440c4f3d-5775-4315-95d8-6628fc419117] succeeded in 0.023760617999869282s: {'triggered': 1} +2026-01-05 06:21:30 WARNING celery.redirected 2026-01-05 06:21:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:21:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:21:30 WARNING celery.redirected 2026-01-05 06:21:30 INFO celery.app.trace Task connecthub.execute_job[fa3315c2-aab6-44e0-bdcd-66ff7b73edcd] succeeded in 0.02010451600108354s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:21:30 INFO celery.app.trace Task connecthub.execute_job[fa3315c2-aab6-44e0-bdcd-66ff7b73edcd] succeeded in 0.02010451600108354s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:22:30 WARNING celery.redirected 2026-01-05 06:22:30 INFO celery.app.trace Task connecthub.dispatcher.tick[9a94cf95-b812-4e01-9361-829af367e990] succeeded in 0.02755342399905203s: {'triggered': 1} +2026-01-05 06:22:30 INFO celery.app.trace Task connecthub.dispatcher.tick[9a94cf95-b812-4e01-9361-829af367e990] succeeded in 0.02755342399905203s: {'triggered': 1} +2026-01-05 06:22:30 WARNING celery.redirected 2026-01-05 06:22:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:22:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:22:30 WARNING celery.redirected 2026-01-05 06:22:30 INFO celery.app.trace Task connecthub.execute_job[7a88f89f-f8ec-43d1-b76f-28c179048781] succeeded in 0.039251813001101254s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:22:30 INFO celery.app.trace Task connecthub.execute_job[7a88f89f-f8ec-43d1-b76f-28c179048781] succeeded in 0.039251813001101254s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:23:30 WARNING celery.redirected 2026-01-05 06:23:30 INFO celery.app.trace Task connecthub.dispatcher.tick[b63c9cb5-f3a3-4df2-b556-9a162dac8d9a] succeeded in 0.026172751000558492s: {'triggered': 1} +2026-01-05 06:23:30 INFO celery.app.trace Task connecthub.dispatcher.tick[b63c9cb5-f3a3-4df2-b556-9a162dac8d9a] succeeded in 0.026172751000558492s: {'triggered': 1} +2026-01-05 06:23:30 WARNING celery.redirected 2026-01-05 06:23:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:23:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:23:30 WARNING celery.redirected 2026-01-05 06:23:30 INFO celery.app.trace Task connecthub.execute_job[1092e43c-9569-44b7-9c41-39fc2d6032d5] succeeded in 0.044292628999755834s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:23:30 INFO celery.app.trace Task connecthub.execute_job[1092e43c-9569-44b7-9c41-39fc2d6032d5] succeeded in 0.044292628999755834s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:24:30 WARNING celery.redirected 2026-01-05 06:24:30 INFO celery.app.trace Task connecthub.dispatcher.tick[8455733b-a460-4267-9434-5920d303749f] succeeded in 0.032748575998994056s: {'triggered': 1} +2026-01-05 06:24:30 INFO celery.app.trace Task connecthub.dispatcher.tick[8455733b-a460-4267-9434-5920d303749f] succeeded in 0.032748575998994056s: {'triggered': 1} +2026-01-05 06:24:30 WARNING celery.redirected 2026-01-05 06:24:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:24:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:24:30 WARNING celery.redirected 2026-01-05 06:24:30 INFO celery.app.trace Task connecthub.execute_job[e157ff3c-a728-4c32-b8ee-55c97d28e67d] succeeded in 0.02652125500026159s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:24:30 INFO celery.app.trace Task connecthub.execute_job[e157ff3c-a728-4c32-b8ee-55c97d28e67d] succeeded in 0.02652125500026159s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:25:30 WARNING celery.redirected 2026-01-05 06:25:30 INFO celery.app.trace Task connecthub.dispatcher.tick[11084af2-2216-4415-80eb-ac2c4aab3a1c] succeeded in 0.026211419999526697s: {'triggered': 1} +2026-01-05 06:25:30 INFO celery.app.trace Task connecthub.dispatcher.tick[11084af2-2216-4415-80eb-ac2c4aab3a1c] succeeded in 0.026211419999526697s: {'triggered': 1} +2026-01-05 06:25:30 WARNING celery.redirected 2026-01-05 06:25:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:25:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:25:30 WARNING celery.redirected 2026-01-05 06:25:30 INFO celery.app.trace Task connecthub.execute_job[81e09ef5-f429-4e1a-b94e-8d3c488ee02c] succeeded in 0.029906062000009115s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:25:30 INFO celery.app.trace Task connecthub.execute_job[81e09ef5-f429-4e1a-b94e-8d3c488ee02c] succeeded in 0.029906062000009115s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:26:30 WARNING celery.redirected 2026-01-05 06:26:30 INFO celery.app.trace Task connecthub.dispatcher.tick[b7d1281f-9ee3-4e30-82cf-eab43c276cd0] succeeded in 0.02654871300001105s: {'triggered': 1} +2026-01-05 06:26:30 INFO celery.app.trace Task connecthub.dispatcher.tick[b7d1281f-9ee3-4e30-82cf-eab43c276cd0] succeeded in 0.02654871300001105s: {'triggered': 1} +2026-01-05 06:26:30 WARNING celery.redirected 2026-01-05 06:26:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:26:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:26:30 WARNING celery.redirected 2026-01-05 06:26:30 INFO celery.app.trace Task connecthub.execute_job[b6037b0f-a0f0-4429-a149-fe67bdf19f37] succeeded in 0.030190688999937265s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:26:30 INFO celery.app.trace Task connecthub.execute_job[b6037b0f-a0f0-4429-a149-fe67bdf19f37] succeeded in 0.030190688999937265s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:27:30 WARNING celery.redirected 2026-01-05 06:27:30 INFO celery.app.trace Task connecthub.dispatcher.tick[fc5566aa-91d5-4e74-9fc9-3466c71afbe3] succeeded in 0.025498818000414758s: {'triggered': 1} +2026-01-05 06:27:30 INFO celery.app.trace Task connecthub.dispatcher.tick[fc5566aa-91d5-4e74-9fc9-3466c71afbe3] succeeded in 0.025498818000414758s: {'triggered': 1} +2026-01-05 06:27:30 WARNING celery.redirected 2026-01-05 06:27:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:27:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:27:30 WARNING celery.redirected 2026-01-05 06:27:30 INFO celery.app.trace Task connecthub.execute_job[f6651311-8d7e-43d1-9b98-0dbf853d973f] succeeded in 0.033761173001039424s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:27:30 INFO celery.app.trace Task connecthub.execute_job[f6651311-8d7e-43d1-9b98-0dbf853d973f] succeeded in 0.033761173001039424s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:28:30 WARNING celery.redirected 2026-01-05 06:28:30 INFO celery.app.trace Task connecthub.dispatcher.tick[626bf215-0b2c-4cbf-b211-dccab018d6c1] succeeded in 0.04751823699916713s: {'triggered': 1} +2026-01-05 06:28:30 INFO celery.app.trace Task connecthub.dispatcher.tick[626bf215-0b2c-4cbf-b211-dccab018d6c1] succeeded in 0.04751823699916713s: {'triggered': 1} +2026-01-05 06:28:30 WARNING celery.redirected 2026-01-05 06:28:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:28:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:28:30 WARNING celery.redirected 2026-01-05 06:28:30 INFO celery.app.trace Task connecthub.execute_job[b2556981-7b27-4df3-b1ef-b8cf7d3f6c4d] succeeded in 0.06460515599974315s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:28:30 INFO celery.app.trace Task connecthub.execute_job[b2556981-7b27-4df3-b1ef-b8cf7d3f6c4d] succeeded in 0.06460515599974315s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:28:52 WARNING celery.redirected 2026-01-05 06:28:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:28:52 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:28:52 WARNING celery.redirected 2026-01-05 06:28:52 INFO celery.app.trace Task connecthub.execute_job[c193401c-800c-4425-9087-e87901a817bc] succeeded in 0.13303516400083026s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:28:52 INFO celery.app.trace Task connecthub.execute_job[c193401c-800c-4425-9087-e87901a817bc] succeeded in 0.13303516400083026s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:29:30 WARNING celery.redirected 2026-01-05 06:29:30 INFO celery.app.trace Task connecthub.dispatcher.tick[580ed651-503b-4d75-8a24-a8a84595f74f] succeeded in 0.03042215200002829s: {'triggered': 1} +2026-01-05 06:29:30 INFO celery.app.trace Task connecthub.dispatcher.tick[580ed651-503b-4d75-8a24-a8a84595f74f] succeeded in 0.03042215200002829s: {'triggered': 1} +2026-01-05 06:29:30 WARNING celery.redirected 2026-01-05 06:29:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:29:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:29:30 WARNING celery.redirected 2026-01-05 06:29:30 INFO celery.app.trace Task connecthub.execute_job[4ac9a96c-2a7e-47cc-9ae0-6f332b465acf] succeeded in 0.033314542000880465s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:29:30 INFO celery.app.trace Task connecthub.execute_job[4ac9a96c-2a7e-47cc-9ae0-6f332b465acf] succeeded in 0.033314542000880465s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:30:30 WARNING celery.redirected 2026-01-05 06:30:30 INFO celery.app.trace Task connecthub.dispatcher.tick[b87c598a-59c9-4b7d-9114-20f088511c61] succeeded in 0.06358310900031938s: {'triggered': 1} +2026-01-05 06:30:30 INFO celery.app.trace Task connecthub.dispatcher.tick[b87c598a-59c9-4b7d-9114-20f088511c61] succeeded in 0.06358310900031938s: {'triggered': 1} +2026-01-05 06:30:31 WARNING celery.redirected 2026-01-05 06:30:31 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:30:31 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:30:31 WARNING celery.redirected 2026-01-05 06:30:31 INFO celery.app.trace Task connecthub.execute_job[96822244-ab0e-406a-9f93-5145615f2364] succeeded in 0.11812609000116936s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:30:31 INFO celery.app.trace Task connecthub.execute_job[96822244-ab0e-406a-9f93-5145615f2364] succeeded in 0.11812609000116936s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:31:30 WARNING celery.redirected 2026-01-05 06:31:30 INFO celery.app.trace Task connecthub.dispatcher.tick[f6a21c3c-c15a-4c72-bc85-a9af82ecaa84] succeeded in 0.03461962300025334s: {'triggered': 1} +2026-01-05 06:31:30 INFO celery.app.trace Task connecthub.dispatcher.tick[f6a21c3c-c15a-4c72-bc85-a9af82ecaa84] succeeded in 0.03461962300025334s: {'triggered': 1} +2026-01-05 06:31:30 WARNING celery.redirected 2026-01-05 06:31:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:31:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:31:30 WARNING celery.redirected 2026-01-05 06:31:30 INFO celery.app.trace Task connecthub.execute_job[882a6ef4-5c7f-4485-9ac5-8ea62451010d] succeeded in 0.03289823999875807s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:31:30 INFO celery.app.trace Task connecthub.execute_job[882a6ef4-5c7f-4485-9ac5-8ea62451010d] succeeded in 0.03289823999875807s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:32:30 WARNING celery.redirected 2026-01-05 06:32:30 INFO celery.app.trace Task connecthub.dispatcher.tick[62bc0c85-0639-4e9c-8fe6-bda9846651ba] succeeded in 0.029510264999771607s: {'triggered': 1} +2026-01-05 06:32:30 INFO celery.app.trace Task connecthub.dispatcher.tick[62bc0c85-0639-4e9c-8fe6-bda9846651ba] succeeded in 0.029510264999771607s: {'triggered': 1} +2026-01-05 06:32:30 WARNING celery.redirected 2026-01-05 06:32:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:32:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:32:30 WARNING celery.redirected 2026-01-05 06:32:30 INFO celery.app.trace Task connecthub.execute_job[281fda63-1885-4aad-be38-5d72d7058d98] succeeded in 0.031486481999309035s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:32:30 INFO celery.app.trace Task connecthub.execute_job[281fda63-1885-4aad-be38-5d72d7058d98] succeeded in 0.031486481999309035s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:33:30 WARNING celery.redirected 2026-01-05 06:33:30 INFO celery.app.trace Task connecthub.dispatcher.tick[05bfd48f-987e-4806-bb99-632c6af0762a] succeeded in 0.009836547000304563s: {'triggered': 1} +2026-01-05 06:33:30 INFO celery.app.trace Task connecthub.dispatcher.tick[05bfd48f-987e-4806-bb99-632c6af0762a] succeeded in 0.009836547000304563s: {'triggered': 1} +2026-01-05 06:33:30 WARNING celery.redirected 2026-01-05 06:33:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:33:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:33:30 WARNING celery.redirected 2026-01-05 06:33:30 INFO celery.app.trace Task connecthub.execute_job[b7328527-b698-4d65-96f0-b095448d0ffa] succeeded in 0.020123803000387852s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:33:30 INFO celery.app.trace Task connecthub.execute_job[b7328527-b698-4d65-96f0-b095448d0ffa] succeeded in 0.020123803000387852s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:34:30 WARNING celery.redirected 2026-01-05 06:34:30 INFO celery.app.trace Task connecthub.dispatcher.tick[f9ce71dd-ba27-46dd-9af8-b1b76b76ad11] succeeded in 0.02779163199920731s: {'triggered': 1} +2026-01-05 06:34:30 INFO celery.app.trace Task connecthub.dispatcher.tick[f9ce71dd-ba27-46dd-9af8-b1b76b76ad11] succeeded in 0.02779163199920731s: {'triggered': 1} +2026-01-05 06:34:30 WARNING celery.redirected 2026-01-05 06:34:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:34:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:34:30 WARNING celery.redirected 2026-01-05 06:34:30 INFO celery.app.trace Task connecthub.execute_job[00519ef6-2be1-47e5-b034-02b77d6faa26] succeeded in 0.023834196999814594s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:34:30 INFO celery.app.trace Task connecthub.execute_job[00519ef6-2be1-47e5-b034-02b77d6faa26] succeeded in 0.023834196999814594s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:35:31 WARNING celery.redirected 2026-01-05 06:35:31 INFO celery.app.trace Task connecthub.dispatcher.tick[1e3ff92b-b16b-4e05-a0f0-145b100a92db] succeeded in 0.1084168569996109s: {'triggered': 1} +2026-01-05 06:35:31 INFO celery.app.trace Task connecthub.dispatcher.tick[1e3ff92b-b16b-4e05-a0f0-145b100a92db] succeeded in 0.1084168569996109s: {'triggered': 1} +2026-01-05 06:35:31 WARNING celery.redirected 2026-01-05 06:35:31 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:35:31 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:35:31 WARNING celery.redirected 2026-01-05 06:35:31 INFO celery.app.trace Task connecthub.execute_job[e140544c-1ee9-4674-abb1-f5a730158a98] succeeded in 0.09931235399926663s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:35:31 INFO celery.app.trace Task connecthub.execute_job[e140544c-1ee9-4674-abb1-f5a730158a98] succeeded in 0.09931235399926663s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:36:30 WARNING celery.redirected 2026-01-05 06:36:30 INFO celery.app.trace Task connecthub.dispatcher.tick[fef082cb-e0d9-4e83-b552-aa7ac0dd4c44] succeeded in 0.04090903100041032s: {'triggered': 1} +2026-01-05 06:36:30 INFO celery.app.trace Task connecthub.dispatcher.tick[fef082cb-e0d9-4e83-b552-aa7ac0dd4c44] succeeded in 0.04090903100041032s: {'triggered': 1} +2026-01-05 06:36:30 WARNING celery.redirected 2026-01-05 06:36:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:36:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:36:30 WARNING celery.redirected 2026-01-05 06:36:30 INFO celery.app.trace Task connecthub.execute_job[24103207-47ad-4749-a459-606da3ae912a] succeeded in 0.06330509800136497s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:36:30 INFO celery.app.trace Task connecthub.execute_job[24103207-47ad-4749-a459-606da3ae912a] succeeded in 0.06330509800136497s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:37:21 WARNING celery.redirected 2026-01-05 06:37:21 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:37:21 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:37:21 WARNING celery.redirected 2026-01-05 06:37:21 INFO celery.app.trace Task connecthub.execute_job[ff067cc1-a57d-4a7a-ad3c-b1b02114e4e8] succeeded in 0.13438932200006093s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:37:21 INFO celery.app.trace Task connecthub.execute_job[ff067cc1-a57d-4a7a-ad3c-b1b02114e4e8] succeeded in 0.13438932200006093s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:37:30 WARNING celery.redirected 2026-01-05 06:37:30 INFO celery.app.trace Task connecthub.dispatcher.tick[c89ec037-53b3-4c3b-bae0-719ad1f0a4ba] succeeded in 0.016763164998337743s: {'triggered': 1} +2026-01-05 06:37:30 INFO celery.app.trace Task connecthub.dispatcher.tick[c89ec037-53b3-4c3b-bae0-719ad1f0a4ba] succeeded in 0.016763164998337743s: {'triggered': 1} +2026-01-05 06:37:30 WARNING celery.redirected 2026-01-05 06:37:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:37:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:37:30 WARNING celery.redirected 2026-01-05 06:37:30 INFO celery.app.trace Task connecthub.execute_job[8fc94f33-cf30-407f-b3be-6176c4018314] succeeded in 0.020661017999373144s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:37:30 INFO celery.app.trace Task connecthub.execute_job[8fc94f33-cf30-407f-b3be-6176c4018314] succeeded in 0.020661017999373144s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:38:30 WARNING celery.redirected 2026-01-05 06:38:30 INFO celery.app.trace Task connecthub.dispatcher.tick[41d0056f-c1c7-489c-a4be-d7b2c0f123f3] succeeded in 0.008013996999579831s: {'triggered': 1} +2026-01-05 06:38:30 INFO celery.app.trace Task connecthub.dispatcher.tick[41d0056f-c1c7-489c-a4be-d7b2c0f123f3] succeeded in 0.008013996999579831s: {'triggered': 1} +2026-01-05 06:38:30 WARNING celery.redirected 2026-01-05 06:38:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:38:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:38:30 WARNING celery.redirected 2026-01-05 06:38:30 INFO celery.app.trace Task connecthub.execute_job[18dca46f-541d-4e73-a1e3-6fbc9605b1af] succeeded in 0.01644616400153609s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:38:30 INFO celery.app.trace Task connecthub.execute_job[18dca46f-541d-4e73-a1e3-6fbc9605b1af] succeeded in 0.01644616400153609s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:39:30 WARNING celery.redirected 2026-01-05 06:39:30 INFO celery.app.trace Task connecthub.dispatcher.tick[a9621a07-ab74-430e-a398-98f226cb9789] succeeded in 0.02474335999977484s: {'triggered': 1} +2026-01-05 06:39:30 INFO celery.app.trace Task connecthub.dispatcher.tick[a9621a07-ab74-430e-a398-98f226cb9789] succeeded in 0.02474335999977484s: {'triggered': 1} +2026-01-05 06:39:30 WARNING celery.redirected 2026-01-05 06:39:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:39:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:39:31 WARNING celery.redirected 2026-01-05 06:39:31 ERROR celery.app.trace Task connecthub.execute_job[e20bbd35-f05f-4990-a452-a519b08fd445] raised unexpected: OperationalError('(sqlite3.OperationalError) table job_logs has no column named run_log') +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context + self.dialect.do_execute( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute + cursor.execute(statement, parameters) +sqlite3.OperationalError: table job_logs has no column named run_log + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/celery/app/trace.py", line 479, in trace_task + R = retval = fun(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/celery/app/trace.py", line 779, in __protected_call__ + return self.run(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/app/app/tasks/execute.py", line 78, in execute_job + crud.create_job_log( + File "/app/app/db/crud.py", line 54, in create_job_log + session.commit() + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2030, in commit + trans.commit(_to_root=True) + File "", line 2, in commit + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go + ret_value = fn(self, *arg, **kw) + ^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 1311, in commit + self._prepare_impl() + File "", line 2, in _prepare_impl + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go + ret_value = fn(self, *arg, **kw) + ^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 1286, in _prepare_impl + self.session.flush() + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 4331, in flush + self._flush(objects) + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 4466, in _flush + with util.safe_reraise(): + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/util/langhelpers.py", line 224, in __exit__ + raise exc_value.with_traceback(exc_tb) + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 4427, in _flush + flush_context.execute() + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/unitofwork.py", line 466, in execute + rec.execute(self) + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/unitofwork.py", line 642, in execute + util.preloaded.orm_persistence.save_obj( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/persistence.py", line 93, in save_obj + _emit_insert_statements( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/persistence.py", line 1233, in _emit_insert_statements + result = connection.execute( + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1419, in execute + return meth( + ^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 527, in _execute_on_connection + return connection._execute_clauseelement( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1641, in _execute_clauseelement + ret = self._execute_context( + ^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context + return self._exec_single_context( + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context + self._handle_dbapi_exception( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2363, in _handle_dbapi_exception + raise sqlalchemy_exception.with_traceback(exc_info[2]) from e + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context + self.dialect.do_execute( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute + cursor.execute(statement, parameters) +sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) table job_logs has no column named run_log +[SQL: INSERT INTO job_logs (job_id, status, snapshot_params, message, traceback, run_log, celery_task_id, attempt, started_at, finished_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)] +[parameters: ('demo.test', 'SUCCESS', '{"job_id": "demo.test", "handler_path": "extensions.example.job:ExampleJob", "public_cfg": {}, "secret_cfg": "", "meta": {"trigger": "celery", "celery_task_id": "e20bbd35-f05f-4990-a452-a519b08fd445", "started_at": "2026-01-05T06:39:30.925320"}}', 'OK', '', '', 'e20bbd35-f05f-4990-a452-a519b08fd445', 0, '2026-01-05 06:39:30.925320', '2026-01-05 06:39:30.988602')] +(Background on this error at: https://sqlalche.me/e/20/e3q8) +2026-01-05 06:39:31 ERROR celery.app.trace Task connecthub.execute_job[e20bbd35-f05f-4990-a452-a519b08fd445] raised unexpected: OperationalError('(sqlite3.OperationalError) table job_logs has no column named run_log') +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context + self.dialect.do_execute( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute + cursor.execute(statement, parameters) +sqlite3.OperationalError: table job_logs has no column named run_log + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "/usr/local/lib/python3.11/site-packages/celery/app/trace.py", line 479, in trace_task + R = retval = fun(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/celery/app/trace.py", line 779, in __protected_call__ + return self.run(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/app/app/tasks/execute.py", line 78, in execute_job + crud.create_job_log( + File "/app/app/db/crud.py", line 54, in create_job_log + session.commit() + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2030, in commit + trans.commit(_to_root=True) + File "", line 2, in commit + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go + ret_value = fn(self, *arg, **kw) + ^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 1311, in commit + self._prepare_impl() + File "", line 2, in _prepare_impl + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go + ret_value = fn(self, *arg, **kw) + ^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 1286, in _prepare_impl + self.session.flush() + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 4331, in flush + self._flush(objects) + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 4466, in _flush + with util.safe_reraise(): + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/util/langhelpers.py", line 224, in __exit__ + raise exc_value.with_traceback(exc_tb) + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 4427, in _flush + flush_context.execute() + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/unitofwork.py", line 466, in execute + rec.execute(self) + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/unitofwork.py", line 642, in execute + util.preloaded.orm_persistence.save_obj( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/persistence.py", line 93, in save_obj + _emit_insert_statements( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/persistence.py", line 1233, in _emit_insert_statements + result = connection.execute( + ^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1419, in execute + return meth( + ^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 527, in _execute_on_connection + return connection._execute_clauseelement( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1641, in _execute_clauseelement + ret = self._execute_context( + ^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context + return self._exec_single_context( + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context + self._handle_dbapi_exception( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2363, in _handle_dbapi_exception + raise sqlalchemy_exception.with_traceback(exc_info[2]) from e + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context + self.dialect.do_execute( + File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 952, in do_execute + cursor.execute(statement, parameters) +sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) table job_logs has no column named run_log +[SQL: INSERT INTO job_logs (job_id, status, snapshot_params, message, traceback, run_log, celery_task_id, attempt, started_at, finished_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)] +[parameters: ('demo.test', 'SUCCESS', '{"job_id": "demo.test", "handler_path": "extensions.example.job:ExampleJob", "public_cfg": {}, "secret_cfg": "", "meta": {"trigger": "celery", "celery_task_id": "e20bbd35-f05f-4990-a452-a519b08fd445", "started_at": "2026-01-05T06:39:30.925320"}}', 'OK', '', '', 'e20bbd35-f05f-4990-a452-a519b08fd445', 0, '2026-01-05 06:39:30.925320', '2026-01-05 06:39:30.988602')] +(Background on this error at: https://sqlalche.me/e/20/e3q8) +2026-01-05 06:40:30 WARNING celery.redirected 2026-01-05 06:40:30 INFO celery.app.trace Task connecthub.dispatcher.tick[416c5cbb-fc83-4b86-84a4-7c865fb38fc5] succeeded in 0.028786763999960385s: {'triggered': 1} +2026-01-05 06:40:30 INFO celery.app.trace Task connecthub.dispatcher.tick[416c5cbb-fc83-4b86-84a4-7c865fb38fc5] succeeded in 0.028786763999960385s: {'triggered': 1} +2026-01-05 06:40:30 WARNING celery.redirected 2026-01-05 06:40:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:40:30 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:40:31 WARNING celery.redirected 2026-01-05 06:40:31 INFO celery.app.trace Task connecthub.execute_job[a97b077e-f751-4c1f-b054-0b1ddd4f24e6] succeeded in 0.07191970299936656s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:40:31 INFO celery.app.trace Task connecthub.execute_job[a97b077e-f751-4c1f-b054-0b1ddd4f24e6] succeeded in 0.07191970299936656s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:47:35 WARNING celery.redirected 2026-01-05 06:47:35 INFO celery.app.trace Task connecthub.dispatcher.tick[d4a2b347-35d3-415c-8fd0-ca42ccd7adcf] succeeded in 0.02985567900032038s: {'triggered': 1} +2026-01-05 06:47:35 INFO celery.app.trace Task connecthub.dispatcher.tick[d4a2b347-35d3-415c-8fd0-ca42ccd7adcf] succeeded in 0.02985567900032038s: {'triggered': 1} +2026-01-05 06:47:35 WARNING celery.redirected 2026-01-05 06:47:35 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:47:35 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:47:35 WARNING celery.redirected 2026-01-05 06:47:35 INFO celery.app.trace Task connecthub.execute_job[b689e3fb-7215-476a-af7f-18fe56560538] succeeded in 0.07674065700120991s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:47:35 INFO celery.app.trace Task connecthub.execute_job[b689e3fb-7215-476a-af7f-18fe56560538] succeeded in 0.07674065700120991s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:48:34 WARNING celery.redirected 2026-01-05 06:48:34 INFO celery.app.trace Task connecthub.dispatcher.tick[1410bb85-aada-4be4-8ee2-23597a82ba6b] succeeded in 0.031015997999929823s: {'triggered': 1} +2026-01-05 06:48:34 INFO celery.app.trace Task connecthub.dispatcher.tick[1410bb85-aada-4be4-8ee2-23597a82ba6b] succeeded in 0.031015997999929823s: {'triggered': 1} +2026-01-05 06:48:34 WARNING celery.redirected 2026-01-05 06:48:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:48:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:48:34 WARNING celery.redirected 2026-01-05 06:48:34 INFO celery.app.trace Task connecthub.execute_job[4966c84e-db3e-4c20-9b64-ca9c2dcdc2b2] succeeded in 0.07551704900106415s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:48:34 INFO celery.app.trace Task connecthub.execute_job[4966c84e-db3e-4c20-9b64-ca9c2dcdc2b2] succeeded in 0.07551704900106415s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:49:34 WARNING celery.redirected 2026-01-05 06:49:34 INFO celery.app.trace Task connecthub.dispatcher.tick[82c16974-155f-4758-a33d-4ad31d3b8bf0] succeeded in 0.01834348900047189s: {'triggered': 1} +2026-01-05 06:49:34 INFO celery.app.trace Task connecthub.dispatcher.tick[82c16974-155f-4758-a33d-4ad31d3b8bf0] succeeded in 0.01834348900047189s: {'triggered': 1} +2026-01-05 06:49:34 WARNING celery.redirected 2026-01-05 06:49:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:49:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:49:34 WARNING celery.redirected 2026-01-05 06:49:34 INFO celery.app.trace Task connecthub.execute_job[5870892b-a902-43fe-8354-4a8afb87afeb] succeeded in 0.02981461799936369s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:49:34 INFO celery.app.trace Task connecthub.execute_job[5870892b-a902-43fe-8354-4a8afb87afeb] succeeded in 0.02981461799936369s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:50:34 WARNING celery.redirected 2026-01-05 06:50:34 INFO celery.app.trace Task connecthub.dispatcher.tick[d16fc9c7-236d-438f-a85a-b5e18478f7ec] succeeded in 0.01405238100051065s: {'triggered': 1} +2026-01-05 06:50:34 INFO celery.app.trace Task connecthub.dispatcher.tick[d16fc9c7-236d-438f-a85a-b5e18478f7ec] succeeded in 0.01405238100051065s: {'triggered': 1} +2026-01-05 06:50:34 WARNING celery.redirected 2026-01-05 06:50:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:50:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:50:34 WARNING celery.redirected 2026-01-05 06:50:34 INFO celery.app.trace Task connecthub.execute_job[cd501a64-fbf8-469d-b350-d6a965f4dd47] succeeded in 0.030117811000309302s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:50:34 INFO celery.app.trace Task connecthub.execute_job[cd501a64-fbf8-469d-b350-d6a965f4dd47] succeeded in 0.030117811000309302s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:51:34 WARNING celery.redirected 2026-01-05 06:51:34 INFO celery.app.trace Task connecthub.dispatcher.tick[fdb68381-fc87-4fa1-b21d-e066250b3163] succeeded in 0.034325909000472166s: {'triggered': 1} +2026-01-05 06:51:34 INFO celery.app.trace Task connecthub.dispatcher.tick[fdb68381-fc87-4fa1-b21d-e066250b3163] succeeded in 0.034325909000472166s: {'triggered': 1} +2026-01-05 06:51:34 WARNING celery.redirected 2026-01-05 06:51:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:51:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:51:34 WARNING celery.redirected 2026-01-05 06:51:34 INFO celery.app.trace Task connecthub.execute_job[381a62de-4919-4703-b1a8-08851807e469] succeeded in 0.07938585500050976s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:51:34 INFO celery.app.trace Task connecthub.execute_job[381a62de-4919-4703-b1a8-08851807e469] succeeded in 0.07938585500050976s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:52:34 INFO celery.app.trace Task connecthub.dispatcher.tick[61695435-4e38-486f-b957-6bb0eb362964] succeeded in 0.034709497000221745s: {'triggered': 1} +2026-01-05 06:52:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:52:34 INFO celery.app.trace Task connecthub.execute_job[cefcf6a0-12c6-4602-a9ae-d3b7a6c5fc08] succeeded in 0.08768268300082127s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:52:37 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:52:37 INFO celery.app.trace Task connecthub.execute_job[d11f5928-0226-4462-8ed1-1b7b50cd8538] succeeded in 0.13126200300030177s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:53:34 INFO celery.app.trace Task connecthub.dispatcher.tick[ecc206a4-b328-4c56-b802-5df9fa55f565] succeeded in 0.018851400000130525s: {'triggered': 1} +2026-01-05 06:53:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:53:34 INFO celery.app.trace Task connecthub.execute_job[99945b54-2f4e-486d-b63c-7209c6ee7522] succeeded in 0.02669546899960551s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:54:34 INFO celery.app.trace Task connecthub.dispatcher.tick[23deecc3-768f-4d86-bd1e-c1e2382ec039] succeeded in 0.021215824001046713s: {'triggered': 1} +2026-01-05 06:54:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:54:34 INFO celery.app.trace Task connecthub.execute_job[3ac26e16-f0b8-431b-b79f-7d634493754a] succeeded in 0.035003455999685684s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:55:34 INFO celery.app.trace Task connecthub.dispatcher.tick[84b8a3c8-df58-405c-be21-5d0e01f6a1a8] succeeded in 0.05902058200081228s: {'triggered': 1} +2026-01-05 06:55:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:55:34 INFO celery.app.trace Task connecthub.execute_job[fa92d18c-7c68-489c-8c20-7af1ffe2736a] succeeded in 0.14188391500101716s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:56:34 INFO celery.app.trace Task connecthub.dispatcher.tick[9ff5237c-d685-465f-82b5-03cb3874c302] succeeded in 0.017588061000424204s: {'triggered': 1} +2026-01-05 06:56:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:56:34 INFO celery.app.trace Task connecthub.execute_job[ac76fb62-55a8-4f8b-a5f6-0e097c73afdc] succeeded in 0.023962750999999116s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:57:34 INFO celery.app.trace Task connecthub.dispatcher.tick[bede20a1-0595-41d9-9740-f67823afd365] succeeded in 0.031636444000469055s: {'triggered': 1} +2026-01-05 06:57:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:57:34 INFO celery.app.trace Task connecthub.execute_job[54f40c83-7540-4ecf-8aca-09c47e9c5c49] succeeded in 0.06714790099977108s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:57:43 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:57:43 INFO celery.app.trace Task connecthub.execute_job[6b0cd59a-b946-494c-ae11-2d775c02a179] succeeded in 0.09546945800138928s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} +2026-01-05 06:58:34 INFO celery.app.trace Task connecthub.dispatcher.tick[143002e6-4b46-4a31-b1f4-70675d23b19f] succeeded in 0.03738421500020195s: {'triggered': 1} +2026-01-05 06:58:34 INFO connecthub.extensions.example ExampleJob ran name=World pong={'ok': True} +2026-01-05 06:58:34 INFO celery.app.trace Task connecthub.execute_job[084d9592-8969-4ab9-b708-22df5f7c68c4] succeeded in 0.048200376999375294s: {'status': 'SUCCESS', 'job_id': 'demo.test', 'result': {'hello': 'World', 'pong': {'ok': True}}, 'message': 'OK'} diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..59f2ca9 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,27 @@ +services: + backend: + environment: + DEV_MODE: "1" + volumes: + - ./app:/app/app + - ./extensions:/app/extensions + + worker: + environment: + DEV_MODE: "1" + volumes: + - ./app:/app/app + - ./extensions:/app/extensions + command: > + sh -c "watchfiles --filter python 'celery -A app.tasks.celery_app:celery_app worker --loglevel=INFO' /app/app /app/extensions" + + beat: + environment: + DEV_MODE: "1" + volumes: + - ./app:/app/app + - ./extensions:/app/extensions + command: > + sh -c "watchfiles --filter python 'celery -A app.tasks.celery_app:celery_app beat --loglevel=INFO' /app/app /app/extensions" + + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..58bda61 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,48 @@ +services: + redis: + image: redis:7-alpine + ports: + - "6379:6379" + + backend: + build: + context: . + dockerfile: docker/Dockerfile + env_file: + - .env + volumes: + - ./data:/data + ports: + - "8000:8000" + command: > + sh -c "if [ \"${DEV_MODE:-0}\" = \"1\" ]; then uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload; else uvicorn app.main:app --host 0.0.0.0 --port 8000; fi" + depends_on: + - redis + + worker: + build: + context: . + dockerfile: docker/Dockerfile + env_file: + - .env + volumes: + - ./data:/data + command: > + sh -c "celery -A app.tasks.celery_app:celery_app worker --loglevel=INFO" + depends_on: + - redis + + beat: + build: + context: . + dockerfile: docker/Dockerfile + env_file: + - .env + volumes: + - ./data:/data + command: > + sh -c "celery -A app.tasks.celery_app:celery_app beat --loglevel=INFO" + depends_on: + - redis + + diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..9efe533 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,22 @@ +FROM python:3.11-slim + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +WORKDIR /app + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +COPY pyproject.toml /app/pyproject.toml + +RUN pip install --no-cache-dir -U pip && \ + pip install --no-cache-dir . + +COPY app /app/app +COPY extensions /app/extensions + +ENV PYTHONPATH=/app + + diff --git a/env.example b/env.example new file mode 100644 index 0000000..2903281 --- /dev/null +++ b/env.example @@ -0,0 +1,9 @@ +APP_NAME=ConnectHub +DATA_DIR=/data +DB_URL=sqlite:////data/connecthub.db +REDIS_URL=redis://redis:6379/0 +FERNET_KEY_PATH=/data/fernet.key +DEV_MODE=1 +LOG_DIR=/data/logs + + diff --git a/extensions/__init__.py b/extensions/__init__.py new file mode 100644 index 0000000..2c2a87c --- /dev/null +++ b/extensions/__init__.py @@ -0,0 +1,3 @@ +# + + diff --git a/extensions/__pycache__/__init__.cpython-312.pyc b/extensions/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..41683f6 Binary files /dev/null and b/extensions/__pycache__/__init__.cpython-312.pyc differ diff --git a/extensions/example/__init__.py b/extensions/example/__init__.py new file mode 100644 index 0000000..2c2a87c --- /dev/null +++ b/extensions/example/__init__.py @@ -0,0 +1,3 @@ +# + + diff --git a/extensions/example/__pycache__/__init__.cpython-312.pyc b/extensions/example/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e973ad3 Binary files /dev/null and b/extensions/example/__pycache__/__init__.cpython-312.pyc differ diff --git a/extensions/example/__pycache__/client.cpython-312.pyc b/extensions/example/__pycache__/client.cpython-312.pyc new file mode 100644 index 0000000..ac97ad4 Binary files /dev/null and b/extensions/example/__pycache__/client.cpython-312.pyc differ diff --git a/extensions/example/__pycache__/job.cpython-312.pyc b/extensions/example/__pycache__/job.cpython-312.pyc new file mode 100644 index 0000000..027ffab Binary files /dev/null and b/extensions/example/__pycache__/job.cpython-312.pyc differ diff --git a/extensions/example/client.py b/extensions/example/client.py new file mode 100644 index 0000000..75874c1 --- /dev/null +++ b/extensions/example/client.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from app.integrations.base import BaseClient + + +class ExampleClient(BaseClient): + """ + 演示用 Client:真实业务中应封装 OA/HR/ERP 的 API。 + 这里不做实际外部请求,仅保留结构与调用方式。 + """ + + def ping(self) -> dict: + # 真实情况:return self.get_json("/ping") + return {"ok": True} + + diff --git a/extensions/example/job.py b/extensions/example/job.py new file mode 100644 index 0000000..f1de62f --- /dev/null +++ b/extensions/example/job.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +import logging +from typing import Any + +from app.jobs.base import BaseJob +from extensions.example.client import ExampleClient + + +logger = logging.getLogger("connecthub.extensions.example") + + +class ExampleJob(BaseJob): + job_id = "example.hello" + + def run(self, params: dict[str, Any], secrets: dict[str, Any]) -> dict[str, Any]: + # params: 明文配置,例如 {"name": "Mars"} + name = params.get("name", "World") + + # secrets: 解密后的明文,例如 {"token": "..."} + token = secrets.get("token", "") + + client = ExampleClient(base_url="https://baidu.com", headers={"Authorization": f"Bearer {token}"}) + try: + pong = client.ping() + + finally: + client.close() + + logger.info("ExampleJob ran name=%s pong=%s", name, pong) + return {"hello": name, "pong": pong} + + diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e37f559 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,33 @@ +[project] +name = "connecthub" +version = "0.1.0" +description = "ConnectHub - lightweight enterprise integration middleware" +requires-python = ">=3.10" +dependencies = [ + "fastapi>=0.110", + "uvicorn[standard]>=0.27", + "sqladmin>=0.16.1", + "sqlalchemy>=2.0", + "pydantic>=2.6", + "pydantic-settings>=2.1", + "cryptography>=41", + "celery>=5.3,<6", + "redis>=5", + "croniter>=2.0", + "httpx>=0.26", + "jinja2>=3.1", + "watchfiles>=0.21", +] + +[build-system] +requires = ["setuptools>=68", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages.find] +where = ["."] +include = ["app*", "extensions*"] + +[tool.uvicorn] +factory = false + +