aury-boot 0.0.14__tar.gz → 0.0.17__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aury_boot-0.0.14 → aury_boot-0.0.17}/PKG-INFO +1 -1
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/_version.py +2 -2
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/app/components.py +50 -3
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/app/startup.py +1 -1
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/config/settings.py +31 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/middleware/logging.py +1 -13
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/AGENTS.md.tpl +12 -0
- aury_boot-0.0.17/aury/boot/commands/templates/project/aury_docs/08-scheduler.md.tpl +178 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/11-logging.md.tpl +5 -41
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/99-cli.md.tpl +1 -1
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/modules/schedules.py.tpl +4 -1
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/common/logging/__init__.py +0 -5
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/common/logging/context.py +1 -52
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/cache/manager.py +5 -1
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/database/manager.py +8 -2
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/scheduler/manager.py +125 -128
- aury_boot-0.0.14/aury/boot/commands/templates/project/aury_docs/08-scheduler.md.tpl +0 -32
- {aury_boot-0.0.14 → aury_boot-0.0.17}/.gitignore +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/README.md +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/adapter/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/adapter/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/adapter/config.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/adapter/decorators.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/adapter/exceptions.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/adapter/http.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/app/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/app/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/app/middlewares.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/config/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/config/multi_instance.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/constants/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/constants/components.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/constants/scheduler.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/constants/service.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/errors/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/errors/chain.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/errors/codes.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/errors/exceptions.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/errors/handlers.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/errors/response.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/interfaces/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/interfaces/egress.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/interfaces/ingress.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/middleware/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/migrations/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/migrations/manager.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/migrations/setup.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/rpc/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/rpc/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/rpc/client.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/rpc/discovery.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/scheduler/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/scheduler/runner.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/application/server/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/add.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/app.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/config.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/docker.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/docs.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/generate.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/init.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/migrate/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/migrate/app.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/migrate/commands.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/pkg.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/scheduler.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/server/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/server/app.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/generate/api.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/generate/model.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/generate/repository.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/generate/schema.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/generate/service.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/README.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/admin_console_init.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/00-overview.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/01-model.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/02-repository.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/03-service.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/04-schema.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/05-api.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/06-exception.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/07-cache.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/09-tasks.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/10-storage.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/12-admin.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/13-channel.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/14-mq.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/15-events.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/16-adapter.md.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/config.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/conftest.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/_header.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/admin.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/cache.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/database.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/log.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/messaging.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/rpc.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/scheduler.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/service.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/storage.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/env_templates/third_party.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/gitignore.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/main.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/modules/api.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/modules/exceptions.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/modules/tasks.py.tpl +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/worker.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/common/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/common/exceptions/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/common/i18n/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/common/i18n/translator.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/common/logging/decorators.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/common/logging/format.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/common/logging/setup.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/contrib/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/contrib/admin_console/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/contrib/admin_console/auth.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/contrib/admin_console/discovery.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/contrib/admin_console/install.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/contrib/admin_console/utils.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/exceptions/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/models/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/models/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/models/mixins.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/models/models.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/pagination/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/repository/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/repository/impl.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/repository/interceptors.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/repository/interface.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/repository/query_builder.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/service/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/service/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/domain/transaction/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/cache/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/cache/backends.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/cache/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/cache/exceptions.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/cache/factory.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/channel/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/channel/backends/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/channel/backends/memory.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/channel/backends/redis.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/channel/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/channel/manager.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/clients/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/clients/rabbitmq/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/clients/rabbitmq/config.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/clients/rabbitmq/manager.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/clients/redis/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/clients/redis/config.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/clients/redis/manager.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/database/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/database/config.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/database/exceptions.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/database/query_tools/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/database/strategies/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/di/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/di/container.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/events/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/events/backends/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/events/backends/memory.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/events/backends/rabbitmq.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/events/backends/redis.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/events/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/events/manager.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/events/middleware.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/monitoring/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/mq/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/mq/backends/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/mq/backends/rabbitmq.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/mq/backends/redis.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/mq/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/mq/manager.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/scheduler/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/scheduler/exceptions.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/storage/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/storage/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/storage/exceptions.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/storage/factory.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/tasks/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/tasks/config.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/tasks/constants.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/tasks/exceptions.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/infrastructure/tasks/manager.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/testing/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/testing/base.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/testing/client.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/testing/factory.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/toolkit/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/toolkit/http/__init__.py +0 -0
- {aury_boot-0.0.14 → aury_boot-0.0.17}/pyproject.toml +0 -0
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
31
|
+
__version__ = version = '0.0.17'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 17)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -281,18 +281,65 @@ class SchedulerComponent(Component):
|
|
|
281
281
|
except Exception as e:
|
|
282
282
|
logger.warning(f"加载定时任务模块失败 ({module_name}): {e}")
|
|
283
283
|
|
|
284
|
+
def _build_scheduler_config(self, config: BaseConfig) -> dict:
|
|
285
|
+
"""根据配置构建 APScheduler 初始化参数。"""
|
|
286
|
+
scheduler_kwargs: dict = {}
|
|
287
|
+
scheduler_config = config.scheduler
|
|
288
|
+
|
|
289
|
+
# jobstores: 根据 URL 自动选择存储后端
|
|
290
|
+
if scheduler_config.jobstore_url:
|
|
291
|
+
url = scheduler_config.jobstore_url
|
|
292
|
+
if url.startswith("redis://"):
|
|
293
|
+
try:
|
|
294
|
+
from apscheduler.jobstores.redis import RedisJobStore
|
|
295
|
+
scheduler_kwargs["jobstores"] = {
|
|
296
|
+
"default": RedisJobStore.from_url(url)
|
|
297
|
+
}
|
|
298
|
+
logger.info(f"调度器使用 Redis 存储: {url.split('@')[-1]}")
|
|
299
|
+
except ImportError:
|
|
300
|
+
logger.warning("Redis jobstore 需要安装 redis: pip install redis")
|
|
301
|
+
else:
|
|
302
|
+
# SQLAlchemy 存储 (sqlite/postgresql/mysql)
|
|
303
|
+
try:
|
|
304
|
+
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
|
|
305
|
+
scheduler_kwargs["jobstores"] = {
|
|
306
|
+
"default": SQLAlchemyJobStore(url=url)
|
|
307
|
+
}
|
|
308
|
+
logger.info("调度器使用 SQLAlchemy 存储")
|
|
309
|
+
except ImportError:
|
|
310
|
+
logger.warning("SQLAlchemy jobstore 需要安装 sqlalchemy")
|
|
311
|
+
|
|
312
|
+
# timezone
|
|
313
|
+
if scheduler_config.timezone:
|
|
314
|
+
scheduler_kwargs["timezone"] = scheduler_config.timezone
|
|
315
|
+
|
|
316
|
+
# job_defaults
|
|
317
|
+
scheduler_kwargs["job_defaults"] = {
|
|
318
|
+
"coalesce": scheduler_config.coalesce,
|
|
319
|
+
"max_instances": scheduler_config.max_instances,
|
|
320
|
+
"misfire_grace_time": scheduler_config.misfire_grace_time,
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return scheduler_kwargs
|
|
324
|
+
|
|
284
325
|
async def setup(self, app: FoundationApp, config: BaseConfig) -> None:
|
|
285
326
|
"""启动调度器。
|
|
286
327
|
|
|
287
|
-
1.
|
|
288
|
-
2.
|
|
328
|
+
1. 根据配置初始化调度器(jobstore/timezone/job_defaults)
|
|
329
|
+
2. 自动发现并加载定时任务模块
|
|
330
|
+
3. 启动调度器(注册装饰器收集的任务)
|
|
289
331
|
"""
|
|
290
332
|
try:
|
|
333
|
+
# 构建配置
|
|
334
|
+
scheduler_kwargs = self._build_scheduler_config(config)
|
|
335
|
+
|
|
336
|
+
# 获取/创建调度器实例
|
|
337
|
+
scheduler = SchedulerManager.get_instance("default", **scheduler_kwargs)
|
|
338
|
+
|
|
291
339
|
# 自动发现并加载定时任务模块
|
|
292
340
|
self._autodiscover_schedules(app, config)
|
|
293
341
|
|
|
294
342
|
# 启动调度器
|
|
295
|
-
scheduler = SchedulerManager.get_instance()
|
|
296
343
|
scheduler.start()
|
|
297
344
|
except Exception as e:
|
|
298
345
|
logger.warning(f"调度器启动失败(非关键): {e}")
|
|
@@ -152,7 +152,7 @@ def collect_component_status() -> list[ComponentStatus]:
|
|
|
152
152
|
name="Cache" if name == "default" else f"Cache [{name}]",
|
|
153
153
|
status="ok",
|
|
154
154
|
backend=instance.backend_type,
|
|
155
|
-
url=instance._config.get("CACHE_URL")
|
|
155
|
+
url=(instance._config or {}).get("CACHE_URL"),
|
|
156
156
|
)
|
|
157
157
|
)
|
|
158
158
|
|
|
@@ -416,6 +416,10 @@ class SchedulerSettings(BaseModel):
|
|
|
416
416
|
- SCHEDULER__ENABLED=false: 只运行 API,不启动调度器
|
|
417
417
|
|
|
418
418
|
独立调度器通过 `aury scheduler` 命令运行,不需要此配置。
|
|
419
|
+
|
|
420
|
+
分布式调度:
|
|
421
|
+
- 配置 SCHEDULER__JOBSTORE_URL 使用 Redis/SQLAlchemy 存储
|
|
422
|
+
- 多节点部署时共享任务状态
|
|
419
423
|
"""
|
|
420
424
|
|
|
421
425
|
enabled: bool = Field(
|
|
@@ -426,6 +430,33 @@ class SchedulerSettings(BaseModel):
|
|
|
426
430
|
default_factory=list,
|
|
427
431
|
description="定时任务模块列表。为空时自动发现 schedules 模块"
|
|
428
432
|
)
|
|
433
|
+
# APScheduler 配置
|
|
434
|
+
jobstore_url: str | None = Field(
|
|
435
|
+
default=None,
|
|
436
|
+
description=(
|
|
437
|
+
"任务存储 URL。支持:\n"
|
|
438
|
+
"- redis://localhost:6379/0(Redis 存储)\n"
|
|
439
|
+
"- sqlite:///jobs.db(SQLite 存储)\n"
|
|
440
|
+
"- postgresql://user:pass@host/db(PostgreSQL 存储)\n"
|
|
441
|
+
"- 不配置则使用内存存储"
|
|
442
|
+
)
|
|
443
|
+
)
|
|
444
|
+
timezone: str | None = Field(
|
|
445
|
+
default=None,
|
|
446
|
+
description="调度器时区,如 Asia/Shanghai、UTC"
|
|
447
|
+
)
|
|
448
|
+
coalesce: bool = Field(
|
|
449
|
+
default=True,
|
|
450
|
+
description="是否合并错过的任务执行(多次错过只执行一次)"
|
|
451
|
+
)
|
|
452
|
+
max_instances: int = Field(
|
|
453
|
+
default=1,
|
|
454
|
+
description="同一任务的最大并发实例数"
|
|
455
|
+
)
|
|
456
|
+
misfire_grace_time: int = Field(
|
|
457
|
+
default=60,
|
|
458
|
+
description="任务错过容忍时间(秒),超过此时间则跳过"
|
|
459
|
+
)
|
|
429
460
|
|
|
430
461
|
|
|
431
462
|
class TaskSettings(BaseModel):
|
|
@@ -17,7 +17,7 @@ from starlette.requests import Request
|
|
|
17
17
|
from starlette.responses import Response
|
|
18
18
|
|
|
19
19
|
from aury.boot.application.errors import global_exception_handler
|
|
20
|
-
from aury.boot.common.logging import
|
|
20
|
+
from aury.boot.common.logging import logger, set_trace_id
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def log_request[T](func: Callable[..., T]) -> Callable[..., T]:
|
|
@@ -187,12 +187,6 @@ class RequestLoggingMiddleware(BaseHTTPMiddleware):
|
|
|
187
187
|
)
|
|
188
188
|
logger.log(log_level.upper(), response_log)
|
|
189
189
|
|
|
190
|
-
# 记录请求上下文(user_id, tenant_id 等用户注册的字段)
|
|
191
|
-
request_contexts = get_request_contexts()
|
|
192
|
-
if request_contexts:
|
|
193
|
-
ctx_str = " | ".join(f"{k}: {v}" for k, v in request_contexts.items())
|
|
194
|
-
logger.info(f"[REQUEST_CONTEXT] Trace-ID: {trace_id} | {ctx_str}")
|
|
195
|
-
|
|
196
190
|
# 写入 access 日志(简洁格式)
|
|
197
191
|
logger.bind(access=True).info(
|
|
198
192
|
f"{request.method} {request.url.path} {status_code} {duration:.3f}s"
|
|
@@ -216,12 +210,6 @@ class RequestLoggingMiddleware(BaseHTTPMiddleware):
|
|
|
216
210
|
f"耗时: {duration:.3f}s | Trace-ID: {trace_id}"
|
|
217
211
|
)
|
|
218
212
|
|
|
219
|
-
# 记录请求上下文(即使异常也要记录,便于追踪问题)
|
|
220
|
-
request_contexts = get_request_contexts()
|
|
221
|
-
if request_contexts:
|
|
222
|
-
ctx_str = " | ".join(f"{k}: {v}" for k, v in request_contexts.items())
|
|
223
|
-
logger.info(f"[REQUEST_CONTEXT] Trace-ID: {trace_id} | {ctx_str}")
|
|
224
|
-
|
|
225
213
|
# 使用全局异常处理器生成响应,而不是直接抛出异常
|
|
226
214
|
# BaseHTTPMiddleware 中直接 raise 会绕过 FastAPI 的异常处理器
|
|
227
215
|
response = await global_exception_handler(request, exc)
|
|
@@ -122,6 +122,8 @@ mypy {package_name}/
|
|
|
122
122
|
|
|
123
123
|
## 代码规范
|
|
124
124
|
|
|
125
|
+
> 项目所有业务配置请通过应用 `settings`/配置对象获取,**不要**直接使用 `os.environ` 在业务代码中读环境变量。
|
|
126
|
+
|
|
125
127
|
### Model 规范
|
|
126
128
|
|
|
127
129
|
- **必须**继承框架预定义基类,**不要**直接继承 `Base`
|
|
@@ -129,6 +131,16 @@ mypy {package_name}/
|
|
|
129
131
|
- 软删除模型**必须**使用复合唯一约束(包含 `deleted_at`),不能单独使用 `unique=True`
|
|
130
132
|
- **不建议**使用数据库外键(`ForeignKey`),通过程序控制关系,便于分库分表和微服务拆分
|
|
131
133
|
|
|
134
|
+
**重要:软删除机制**
|
|
135
|
+
|
|
136
|
+
框架采用「默认 0」策略,而非 IS NULL:
|
|
137
|
+
- `deleted_at = 0`:未删除
|
|
138
|
+
- `deleted_at > 0`:已删除(Unix 时间戳)
|
|
139
|
+
|
|
140
|
+
查询未删除记录时,使用 `WHERE deleted_at = 0`,不是 `WHERE deleted_at IS NULL`。
|
|
141
|
+
|
|
142
|
+
BaseRepository 已自动处理软删除过滤,无需手动添加条件。
|
|
143
|
+
|
|
132
144
|
```python
|
|
133
145
|
# ✅ 正确
|
|
134
146
|
from aury.boot.domain.models import AuditableStateModel
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# 定时任务(Scheduler)
|
|
2
|
+
|
|
3
|
+
基于 APScheduler,完全透传原生 API。
|
|
4
|
+
|
|
5
|
+
## 基本用法
|
|
6
|
+
|
|
7
|
+
**文件**: `{package_name}/schedules/__init__.py`
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
"""定时任务模块。"""
|
|
11
|
+
|
|
12
|
+
from apscheduler.triggers.cron import CronTrigger
|
|
13
|
+
from apscheduler.triggers.interval import IntervalTrigger
|
|
14
|
+
|
|
15
|
+
from aury.boot.common.logging import logger
|
|
16
|
+
from aury.boot.infrastructure.scheduler import SchedulerManager
|
|
17
|
+
|
|
18
|
+
scheduler = SchedulerManager.get_instance()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@scheduler.scheduled_job(IntervalTrigger(seconds=60))
|
|
22
|
+
async def every_minute():
|
|
23
|
+
"""每 60 秒执行。"""
|
|
24
|
+
logger.info("定时任务执行中...")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@scheduler.scheduled_job(CronTrigger(hour=0, minute=0))
|
|
28
|
+
async def daily_task():
|
|
29
|
+
"""每天凌晨执行。"""
|
|
30
|
+
logger.info("每日任务执行中...")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@scheduler.scheduled_job(CronTrigger(day_of_week="mon", hour=9))
|
|
34
|
+
async def weekly_report():
|
|
35
|
+
"""每周一 9 点执行。"""
|
|
36
|
+
logger.info("周报任务执行中...")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
启用方式:配置 `SCHEDULER__ENABLED=true`,框架自动加载 `{package_name}/schedules/` 模块。
|
|
40
|
+
|
|
41
|
+
## 配置项
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# .env
|
|
45
|
+
SCHEDULER__ENABLED=true # 是否启用
|
|
46
|
+
SCHEDULER__TIMEZONE=Asia/Shanghai # 时区
|
|
47
|
+
SCHEDULER__COALESCE=true # 合并错过的任务
|
|
48
|
+
SCHEDULER__MAX_INSTANCES=1 # 同一任务最大并发数
|
|
49
|
+
SCHEDULER__MISFIRE_GRACE_TIME=60 # 错过容忍时间(秒)
|
|
50
|
+
SCHEDULER__JOBSTORE_URL=redis://localhost:6379/0 # 分布式存储(可选)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 触发器类型
|
|
54
|
+
|
|
55
|
+
### CronTrigger - 定时触发
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from apscheduler.triggers.cron import CronTrigger
|
|
59
|
+
|
|
60
|
+
# 每天凌晨 2:30
|
|
61
|
+
CronTrigger(hour=2, minute=30)
|
|
62
|
+
|
|
63
|
+
# 每小时整点
|
|
64
|
+
CronTrigger(hour="*", minute=0)
|
|
65
|
+
|
|
66
|
+
# 工作日 9:00
|
|
67
|
+
CronTrigger(day_of_week="mon-fri", hour=9)
|
|
68
|
+
|
|
69
|
+
# 每月 1 号
|
|
70
|
+
CronTrigger(day=1, hour=0)
|
|
71
|
+
|
|
72
|
+
# 使用 crontab 表达式
|
|
73
|
+
CronTrigger.from_crontab("0 2 * * *") # 每天 2:00
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### IntervalTrigger - 间隔触发
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from apscheduler.triggers.interval import IntervalTrigger
|
|
80
|
+
|
|
81
|
+
IntervalTrigger(seconds=30) # 每 30 秒
|
|
82
|
+
IntervalTrigger(minutes=5) # 每 5 分钟
|
|
83
|
+
IntervalTrigger(hours=1) # 每小时
|
|
84
|
+
IntervalTrigger(days=1) # 每天
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### DateTrigger - 一次性触发
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from apscheduler.triggers.date import DateTrigger
|
|
91
|
+
from datetime import datetime, timedelta
|
|
92
|
+
|
|
93
|
+
# 10 秒后执行
|
|
94
|
+
DateTrigger(run_date=datetime.now() + timedelta(seconds=10))
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 多实例支持
|
|
98
|
+
|
|
99
|
+
支持不同业务线使用独立的调度器实例:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
# 默认实例
|
|
103
|
+
scheduler = SchedulerManager.get_instance()
|
|
104
|
+
|
|
105
|
+
# 命名实例
|
|
106
|
+
report_scheduler = SchedulerManager.get_instance("report")
|
|
107
|
+
cleanup_scheduler = SchedulerManager.get_instance("cleanup")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 分布式调度
|
|
111
|
+
|
|
112
|
+
多节点部署时,配置相同的 `SCHEDULER__JOBSTORE_URL`,所有节点共享任务状态:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# 所有节点使用相同配置
|
|
116
|
+
SCHEDULER__JOBSTORE_URL=redis://redis:6379/0
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
APScheduler 自动协调防止重复执行。
|
|
120
|
+
|
|
121
|
+
### 代码方式配置(高级)
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from apscheduler.jobstores.redis import RedisJobStore
|
|
125
|
+
from apscheduler.executors.asyncio import AsyncIOExecutor
|
|
126
|
+
|
|
127
|
+
scheduler = SchedulerManager.get_instance(
|
|
128
|
+
"distributed",
|
|
129
|
+
jobstores={"default": RedisJobStore(host="localhost", port=6379)},
|
|
130
|
+
executors={"default": AsyncIOExecutor()},
|
|
131
|
+
job_defaults={"coalesce": True, "max_instances": 1},
|
|
132
|
+
timezone="Asia/Shanghai",
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## 任务管理
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# 添加任务
|
|
140
|
+
scheduler.add_job(my_task, CronTrigger(hour=2), id="my_task")
|
|
141
|
+
|
|
142
|
+
# 获取任务
|
|
143
|
+
job = scheduler.get_job("my_task")
|
|
144
|
+
jobs = scheduler.get_jobs()
|
|
145
|
+
|
|
146
|
+
# 暂停/恢复
|
|
147
|
+
scheduler.pause_job("my_task")
|
|
148
|
+
scheduler.resume_job("my_task")
|
|
149
|
+
|
|
150
|
+
# 移除
|
|
151
|
+
scheduler.remove_job("my_task")
|
|
152
|
+
|
|
153
|
+
# 重新调度
|
|
154
|
+
scheduler.reschedule_job("my_task", CronTrigger(hour=3))
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 监听器(高级)
|
|
158
|
+
|
|
159
|
+
通过底层 APScheduler 实例访问:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
|
|
163
|
+
|
|
164
|
+
def job_listener(event):
|
|
165
|
+
if event.exception:
|
|
166
|
+
logger.error(f"任务失败: {event.job_id}")
|
|
167
|
+
else:
|
|
168
|
+
logger.info(f"任务完成: {event.job_id}")
|
|
169
|
+
|
|
170
|
+
scheduler.scheduler.add_listener(job_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## 实践建议
|
|
174
|
+
|
|
175
|
+
1. **使用明确的 ID**:便于管理和调试
|
|
176
|
+
2. **合理设置间隔**:避免太频繁的任务
|
|
177
|
+
3. **异常处理**:在任务函数内捕获异常,避免影响调度器
|
|
178
|
+
4. **超时保护**:长运行任务使用 `asyncio.wait_for`
|
|
@@ -19,44 +19,8 @@ logger.exception("异常信息") # 自动记录堆栈
|
|
|
19
19
|
2024-01-15 12:00:00 | INFO | app.service:create:42 | abc123 - 操作成功
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
## 11.2 注入用户信息
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```python
|
|
27
|
-
# app/auth/context.py
|
|
28
|
-
from contextvars import ContextVar
|
|
29
|
-
from aury.boot.common.logging import register_request_context
|
|
30
|
-
|
|
31
|
-
_user_id: ContextVar[str] = ContextVar("user_id", default="")
|
|
32
|
-
|
|
33
|
-
def set_user_id(uid: str) -> None:
|
|
34
|
-
_user_id.set(uid)
|
|
35
|
-
|
|
36
|
-
# 启动时注册(只需一次)
|
|
37
|
-
register_request_context("user_id", _user_id.get)
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
在认证中间件中设置(order < 100 以在日志中间件前执行):
|
|
41
|
-
|
|
42
|
-
```python
|
|
43
|
-
class AuthMiddleware(Middleware):
|
|
44
|
-
order = 50 # 在日志中间件(order=100)之前执行
|
|
45
|
-
|
|
46
|
-
async def dispatch(self, request, call_next):
|
|
47
|
-
user = await verify_token(request)
|
|
48
|
-
if user:
|
|
49
|
-
set_user_id(str(user.id))
|
|
50
|
-
return await call_next(request)
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
结果:
|
|
54
|
-
```
|
|
55
|
-
← GET /api/users | 状态: 200 | 耗时: 0.05s | Trace-ID: abc123
|
|
56
|
-
[REQUEST_CONTEXT] Trace-ID: abc123 | user_id: 123
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## 11.3 性能监控装饰器
|
|
23
|
+
## 11.2 性能监控装饰器
|
|
60
24
|
|
|
61
25
|
```python
|
|
62
26
|
from aury.boot.common.logging import log_performance, log_exceptions
|
|
@@ -70,7 +34,7 @@ async def risky_operation():
|
|
|
70
34
|
...
|
|
71
35
|
```
|
|
72
36
|
|
|
73
|
-
## 11.
|
|
37
|
+
## 11.3 HTTP 请求日志
|
|
74
38
|
|
|
75
39
|
框架内置 `RequestLoggingMiddleware` 自动记录:
|
|
76
40
|
|
|
@@ -85,7 +49,7 @@ async def risky_operation():
|
|
|
85
49
|
慢请求: GET /api/reports | 耗时: 2.345s (超过1秒) | Trace-ID: abc123
|
|
86
50
|
```
|
|
87
51
|
|
|
88
|
-
## 11.
|
|
52
|
+
## 11.4 自定义日志文件
|
|
89
53
|
|
|
90
54
|
为特定业务创建独立的日志文件:
|
|
91
55
|
|
|
@@ -99,7 +63,7 @@ register_log_sink("payment", filter_key="payment")
|
|
|
99
63
|
logger.bind(payment=True).info(f"支付成功 | 订单: {{order_id}}")
|
|
100
64
|
```
|
|
101
65
|
|
|
102
|
-
## 11.
|
|
66
|
+
## 11.5 异步任务链路追踪
|
|
103
67
|
|
|
104
68
|
跨进程任务需要手动传递 trace_id:
|
|
105
69
|
|
|
@@ -117,7 +81,7 @@ async def process_order(order_id: str, trace_id: str | None = None):
|
|
|
117
81
|
logger.info(f"处理订单: {{order_id}}") # 自动包含 trace_id
|
|
118
82
|
```
|
|
119
83
|
|
|
120
|
-
## 11.
|
|
84
|
+
## 11.6 服务上下文隔离
|
|
121
85
|
|
|
122
86
|
日志自动按服务类型分离:
|
|
123
87
|
|
{aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/aury_docs/99-cli.md.tpl
RENAMED
|
@@ -153,7 +153,7 @@ register_commands(app)
|
|
|
153
153
|
@app.command()
|
|
154
154
|
async def hello(name: str = "world") -> None:
|
|
155
155
|
"""示例:项目自定义命令。"""
|
|
156
|
-
print(f"Hello, {name} from {project_name_snake}!")
|
|
156
|
+
print(f"Hello, {{name}} from {project_name_snake}!")
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
> 注意:这里的 `app` 是 Typer 应用实例,`register_commands` 会把所有内置的 `init/generate/server/...` 等命令挂到你自己的 CLI 下。
|
{aury_boot-0.0.14 → aury_boot-0.0.17}/aury/boot/commands/templates/project/modules/schedules.py.tpl
RENAMED
|
@@ -6,13 +6,16 @@
|
|
|
6
6
|
也可通过 SCHEDULER_SCHEDULE_MODULES 环境变量指定自定义模块。
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
# from apscheduler.triggers.cron import CronTrigger
|
|
10
|
+
# from apscheduler.triggers.interval import IntervalTrigger
|
|
11
|
+
#
|
|
9
12
|
# from aury.boot.common.logging import logger
|
|
10
13
|
# from aury.boot.infrastructure.scheduler import SchedulerManager
|
|
11
14
|
#
|
|
12
15
|
# scheduler = SchedulerManager.get_instance()
|
|
13
16
|
#
|
|
14
17
|
#
|
|
15
|
-
# @scheduler.scheduled_job(
|
|
18
|
+
# @scheduler.scheduled_job(IntervalTrigger(seconds=60))
|
|
16
19
|
# async def example_job():
|
|
17
20
|
# """示例定时任务,每 60 秒执行一次。"""
|
|
18
21
|
# logger.info("定时任务执行中...")
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
- 性能监控装饰器
|
|
6
6
|
- 异常日志装饰器
|
|
7
7
|
- 链路追踪 ID 支持
|
|
8
|
-
- 请求上下文注入(user_id 等)
|
|
9
8
|
- 自定义日志 sink 注册 API
|
|
10
9
|
|
|
11
10
|
日志文件:
|
|
@@ -27,10 +26,8 @@ logger.remove()
|
|
|
27
26
|
# 从子模块导入
|
|
28
27
|
from aury.boot.common.logging.context import (
|
|
29
28
|
ServiceContext,
|
|
30
|
-
get_request_contexts,
|
|
31
29
|
get_service_context,
|
|
32
30
|
get_trace_id,
|
|
33
|
-
register_request_context,
|
|
34
31
|
set_service_context,
|
|
35
32
|
set_trace_id,
|
|
36
33
|
)
|
|
@@ -55,7 +52,6 @@ __all__ = [
|
|
|
55
52
|
"ServiceContext",
|
|
56
53
|
"format_exception_java_style",
|
|
57
54
|
"get_class_logger",
|
|
58
|
-
"get_request_contexts",
|
|
59
55
|
"get_service_context",
|
|
60
56
|
"get_trace_id",
|
|
61
57
|
"log_exception",
|
|
@@ -63,7 +59,6 @@ __all__ = [
|
|
|
63
59
|
"log_performance",
|
|
64
60
|
"logger",
|
|
65
61
|
"register_log_sink",
|
|
66
|
-
"register_request_context",
|
|
67
62
|
"set_service_context",
|
|
68
63
|
"set_trace_id",
|
|
69
64
|
"setup_intercept",
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"""日志上下文管理。
|
|
2
2
|
|
|
3
|
-
提供链路追踪 ID
|
|
3
|
+
提供链路追踪 ID、服务上下文的管理。
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
from collections.abc import Callable
|
|
9
8
|
from contextvars import ContextVar
|
|
10
9
|
from enum import Enum
|
|
11
10
|
import uuid
|
|
@@ -24,8 +23,6 @@ _service_context: ContextVar[ServiceContext] = ContextVar("service_context", def
|
|
|
24
23
|
# 链路追踪 ID
|
|
25
24
|
_trace_id_var: ContextVar[str] = ContextVar("trace_id", default="")
|
|
26
25
|
|
|
27
|
-
# 请求上下文字段注册表(用户可注册自定义字段,如 user_id, tenant_id)
|
|
28
|
-
_request_context_getters: dict[str, Callable[[], str]] = {}
|
|
29
26
|
|
|
30
27
|
|
|
31
28
|
def get_service_context() -> ServiceContext:
|
|
@@ -75,58 +72,10 @@ def set_trace_id(trace_id: str) -> None:
|
|
|
75
72
|
_trace_id_var.set(trace_id)
|
|
76
73
|
|
|
77
74
|
|
|
78
|
-
def register_request_context(name: str, getter: Callable[[], str]) -> None:
|
|
79
|
-
"""注册请求上下文字段。
|
|
80
|
-
|
|
81
|
-
注册后,该字段会在每个请求结束时记录一次(与 trace_id 关联)。
|
|
82
|
-
适用于 user_id、tenant_id 等需要关联到请求但不需要每行日志都记录的信息。
|
|
83
|
-
|
|
84
|
-
Args:
|
|
85
|
-
name: 字段名(如 "user_id", "tenant_id")
|
|
86
|
-
getter: 获取当前值的函数(通常从 ContextVar 读取)
|
|
87
|
-
|
|
88
|
-
使用示例:
|
|
89
|
-
from contextvars import ContextVar
|
|
90
|
-
from aury.boot.common.logging import register_request_context
|
|
91
|
-
|
|
92
|
-
# 定义上下文变量
|
|
93
|
-
_user_id: ContextVar[str] = ContextVar("user_id", default="")
|
|
94
|
-
|
|
95
|
-
def set_user_id(uid: str):
|
|
96
|
-
_user_id.set(uid)
|
|
97
|
-
|
|
98
|
-
# 启动时注册(一次)
|
|
99
|
-
register_request_context("user_id", _user_id.get)
|
|
100
|
-
|
|
101
|
-
# Auth 中间件中设置(每次请求)
|
|
102
|
-
set_user_id(str(user.id))
|
|
103
|
-
"""
|
|
104
|
-
_request_context_getters[name] = getter
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def get_request_contexts() -> dict[str, str]:
|
|
108
|
-
"""获取所有已注册的请求上下文当前值。
|
|
109
|
-
|
|
110
|
-
Returns:
|
|
111
|
-
字段名到值的字典(仅包含非空值)
|
|
112
|
-
"""
|
|
113
|
-
result = {}
|
|
114
|
-
for name, getter in _request_context_getters.items():
|
|
115
|
-
try:
|
|
116
|
-
value = getter()
|
|
117
|
-
if value: # 只包含非空值
|
|
118
|
-
result[name] = value
|
|
119
|
-
except Exception:
|
|
120
|
-
pass # 忽略获取失败的字段
|
|
121
|
-
return result
|
|
122
|
-
|
|
123
|
-
|
|
124
75
|
__all__ = [
|
|
125
76
|
"ServiceContext",
|
|
126
|
-
"get_request_contexts",
|
|
127
77
|
"get_service_context",
|
|
128
78
|
"get_trace_id",
|
|
129
|
-
"register_request_context",
|
|
130
79
|
"set_service_context",
|
|
131
80
|
"set_trace_id",
|
|
132
81
|
]
|
|
@@ -110,13 +110,15 @@ class CacheManager:
|
|
|
110
110
|
supported = ", ".join(b.value for b in CacheBackend)
|
|
111
111
|
raise ValueError(f"不支持的缓存后端: {backend}。支持: {supported}")
|
|
112
112
|
|
|
113
|
-
#
|
|
113
|
+
# 保存配置(用于启动横幅等场景展示)
|
|
114
114
|
self._config = {"CACHE_TYPE": backend.value}
|
|
115
115
|
|
|
116
116
|
# 根据后端类型构建配置并创建后端
|
|
117
117
|
if backend == CacheBackend.REDIS:
|
|
118
118
|
if not url:
|
|
119
119
|
raise ValueError("Redis 缓存需要提供 url 参数")
|
|
120
|
+
# 记录 URL 以便在启动横幅中展示(会通过 mask_url 脱敏)
|
|
121
|
+
self._config["CACHE_URL"] = url
|
|
120
122
|
self._backend = await CacheFactory.create(
|
|
121
123
|
"redis", url=url, serializer=serializer
|
|
122
124
|
)
|
|
@@ -128,6 +130,8 @@ class CacheManager:
|
|
|
128
130
|
cache_url = url or (servers[0] if servers else None)
|
|
129
131
|
if not cache_url:
|
|
130
132
|
raise ValueError("Memcached 缓存需要提供 url 参数")
|
|
133
|
+
# 同样记录 URL,便于在启动横幅中展示
|
|
134
|
+
self._config["CACHE_URL"] = cache_url
|
|
131
135
|
self._backend = await CacheFactory.create(
|
|
132
136
|
"memcached", servers=cache_url
|
|
133
137
|
)
|
|
@@ -10,7 +10,7 @@ from collections.abc import AsyncGenerator
|
|
|
10
10
|
from contextlib import asynccontextmanager
|
|
11
11
|
|
|
12
12
|
from sqlalchemy import text
|
|
13
|
-
from sqlalchemy.exc import DisconnectionError, OperationalError
|
|
13
|
+
from sqlalchemy.exc import DisconnectionError, OperationalError, SQLAlchemyError
|
|
14
14
|
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine
|
|
15
15
|
|
|
16
16
|
from aury.boot.common.logging import logger
|
|
@@ -237,10 +237,16 @@ class DatabaseManager:
|
|
|
237
237
|
try:
|
|
238
238
|
await self._check_session_connection(session)
|
|
239
239
|
yield session
|
|
240
|
-
except
|
|
240
|
+
except SQLAlchemyError as exc:
|
|
241
|
+
# 只捕获数据库相关异常
|
|
241
242
|
await session.rollback()
|
|
242
243
|
logger.exception(f"数据库会话异常: {exc}")
|
|
243
244
|
raise
|
|
245
|
+
except Exception:
|
|
246
|
+
# 非数据库异常(如请求验证错误):仍需回滚以确保事务一致性
|
|
247
|
+
# 但不记录为数据库异常,直接传播
|
|
248
|
+
await session.rollback()
|
|
249
|
+
raise
|
|
244
250
|
finally:
|
|
245
251
|
await session.close()
|
|
246
252
|
|