claude-flow-novice 2.15.3 → 2.15.4
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.
- package/.claude/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
- package/.claude/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
- package/.claude/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
- package/.claude/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
- package/.claude/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
- package/.claude/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
- package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
- package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
- package/.claude/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
- package/.claude/commands/cfn-loop-cli.md +16 -2
- package/.claude/commands/switch-api.md +31 -10
- package/.claude/hooks/cfn-lint-sql-injection.sh +61 -0
- package/.claude/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
- package/.claude/hooks/cfn-pre-edit-security-warning.sh +40 -0
- package/.claude/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
- package/.claude/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
- package/.claude/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
- package/.claude/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
- package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
- package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +184 -23
- package/.claude/skills/cfn-loop-orchestration/security_utils.sh +24 -0
- package/.claude/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
- package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
- package/.claude/skills/cfn-redis-coordination/agent-log.sh +4 -0
- package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
- package/.claude/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
- package/.claude/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
- package/.claude/skills/cfn-redis-coordination/get-context.sh +33 -0
- package/.claude/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
- package/.claude/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -0
- package/.claude/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
- package/.claude/skills/cfn-redis-coordination/redis-functions.sh +33 -0
- package/.claude/skills/cfn-redis-coordination/report-completion.sh +24 -31
- package/.claude/skills/cfn-redis-coordination/store-context.sh +4 -0
- package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
- package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
- package/.claude/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
- package/.claude/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
- package/.claude/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
- package/README.md +116 -475
- package/claude-assets/agents/cfn-dev-team/README.md +103 -0
- package/claude-assets/agents/cfn-dev-team/architecture/goal-planner.md +1 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +77 -15
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +355 -6
- package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +82 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +82 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +77 -15
- package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +99 -12
- package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +1 -1
- package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +97 -0
- package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +20 -1
- package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +97 -0
- package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +110 -13
- package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +106 -15
- package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +115 -11
- package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +94 -7
- package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +87 -9
- package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +85 -7
- package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +160 -28
- package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +101 -19
- package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +108 -14
- package/claude-assets/agents/cfn-dev-team/reviewers/{reviewer.md → code-reviewer.md} +95 -8
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +107 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +98 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +95 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +136 -9
- package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +108 -1
- package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +107 -13
- package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +737 -0
- package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +828 -0
- package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +106 -7
- package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +77 -0
- package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +684 -0
- package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +110 -1
- package/claude-assets/agents/cfn-dev-team/testers/tester.md +94 -7
- package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +1 -3
- package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +87 -13
- package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +103 -7
- package/claude-assets/agents/cfn-dev-team/utility/researcher.md +1 -3
- package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +94 -7
- package/claude-assets/agents/docker-coordinators/cfn-docker-v3-coordinator.md +46 -0
- package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
- package/claude-assets/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
- package/claude-assets/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
- package/claude-assets/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
- package/claude-assets/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
- package/claude-assets/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
- package/claude-assets/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
- package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
- package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
- package/claude-assets/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
- package/claude-assets/commands/cfn-loop-cli.md +16 -2
- package/claude-assets/commands/switch-api.md +31 -10
- package/claude-assets/hooks/cfn-lint-sql-injection.sh +61 -0
- package/claude-assets/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
- package/claude-assets/hooks/cfn-pre-edit-security-warning.sh +40 -0
- package/claude-assets/hooks/detect-hardcoded-credentials.sh +212 -0
- package/claude-assets/skills/SKILL_TEMPLATE.md +774 -0
- package/claude-assets/skills/agent-lifecycle/execute-lifecycle-hook.sh +84 -113
- package/claude-assets/skills/agent-lifecycle/simple-audit.sh +33 -6
- package/claude-assets/skills/agent-template-generator/SKILL.md +440 -0
- package/claude-assets/skills/agent-template-generator/generate-agent.sh +405 -0
- package/claude-assets/skills/agent-validation-linter/SKILL.md +589 -0
- package/claude-assets/skills/agent-validation-linter/lint-agents.sh +271 -0
- package/claude-assets/skills/bootstrap/bash-fundamentals.md +786 -0
- package/claude-assets/skills/bootstrap/database-connection.md +464 -0
- package/claude-assets/skills/bootstrap/error-handling.md +580 -0
- package/claude-assets/skills/bootstrap/file-operations.md +699 -0
- package/claude-assets/skills/bootstrap/skill-loader.md +616 -0
- package/claude-assets/skills/bootstrap/sqlite-params.sh +287 -0
- package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
- package/claude-assets/skills/cfn-automatic-memory-persistence/test-memory-persistence.sh +17 -16
- package/claude-assets/skills/cfn-deployment/SKILL.md +293 -0
- package/claude-assets/skills/cfn-deployment/execute.sh +21 -0
- package/claude-assets/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
- package/claude-assets/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
- package/claude-assets/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
- package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +38 -0
- package/claude-assets/skills/cfn-error-batching-strategy/lib/core-functions.sh +47 -47
- package/claude-assets/skills/cfn-file-operations/SKILL.md +290 -0
- package/claude-assets/skills/cfn-file-operations/execute.sh +129 -0
- package/claude-assets/skills/cfn-file-operations/lib/atomic-write.sh +294 -0
- package/claude-assets/skills/cfn-file-operations/lib/lock.sh +361 -0
- package/claude-assets/skills/cfn-file-operations/test.sh +369 -0
- package/claude-assets/skills/cfn-log-operations/SKILL.md +308 -0
- package/claude-assets/skills/cfn-log-operations/execute.sh +420 -0
- package/claude-assets/skills/cfn-log-operations/lib/rotate.sh +406 -0
- package/claude-assets/skills/cfn-log-operations/lib/search.sh +448 -0
- package/claude-assets/skills/cfn-log-operations/test.sh +394 -0
- package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
- package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +184 -23
- package/claude-assets/skills/cfn-loop-orchestration/security_utils.sh +24 -0
- package/claude-assets/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
- package/claude-assets/skills/cfn-parameterized-queries/SKILL.md +339 -0
- package/claude-assets/skills/cfn-playbook/query-playbook.sh +19 -15
- package/claude-assets/skills/cfn-playbook/update-playbook.sh +25 -14
- package/claude-assets/skills/cfn-process-instrumentation/instrument-process.sh +44 -0
- package/claude-assets/skills/cfn-promotion/SKILL.md +305 -0
- package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-log.sh +4 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
- package/claude-assets/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
- package/claude-assets/skills/cfn-redis-coordination/get-context.sh +33 -0
- package/claude-assets/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
- package/claude-assets/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -0
- package/claude-assets/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
- package/claude-assets/skills/cfn-redis-coordination/redis-functions.sh +33 -0
- package/claude-assets/skills/cfn-redis-coordination/report-completion.sh +24 -31
- package/claude-assets/skills/cfn-redis-coordination/store-context.sh +4 -0
- package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
- package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
- package/claude-assets/skills/cfn-skill-loader/SKILL.md +466 -0
- package/claude-assets/skills/cfn-skill-loader/execute.sh +344 -0
- package/claude-assets/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
- package/claude-assets/skills/cfn-task-audit/get-audit-data.sh +42 -21
- package/claude-assets/skills/cfn-task-audit/store-task-audit.sh +17 -10
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh +17 -14
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh.backup-1763392821 +55 -0
- package/claude-assets/skills/cfn-test-runner/store-benchmarks.sh +17 -19
- package/claude-assets/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
- package/claude-assets/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
- package/claude-assets/skills/cfn-utilities/SKILL.md +237 -0
- package/claude-assets/skills/cfn-utilities/execute.sh +32 -0
- package/claude-assets/skills/cfn-utilities/lib/errors.sh +56 -0
- package/claude-assets/skills/cfn-utilities/lib/file-ops.sh +164 -0
- package/claude-assets/skills/cfn-utilities/lib/logging.sh +77 -0
- package/claude-assets/skills/cfn-utilities/lib/retry.sh +127 -0
- package/claude-assets/skills/cfn-utilities/test.sh +317 -0
- package/claude-assets/skills/integration/agent-handoff.sh +62 -64
- package/claude-assets/skills/json-validation/SKILL.md +431 -0
- package/claude-assets/skills/json-validation/test-validate-success-criteria.sh +421 -0
- package/claude-assets/skills/json-validation/validate-success-criteria.sh +197 -0
- package/claude-assets/skills/redis-coordination/validate-parameters.sh +34 -0
- package/claude-assets/skills/workflow-codification/DEPLOY_QUICK_REFERENCE.md +106 -0
- package/claude-assets/skills/workflow-codification/PROPAGATE_UPDATE_QUICK_REFERENCE.md +366 -0
- package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh +481 -0
- package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh.backup-1763392820 +512 -0
- package/claude-assets/skills/workflow-codification/lib/security-utils.sh +204 -0
- package/claude-assets/skills/workflow-codification/propagate-skill-update.sh +648 -0
- package/claude-assets/skills/workflow-codification/propagate-skill-update.sh.backup-1763392820 +664 -0
- package/claude-assets/skills/workflow-codification/test-integration.sh +15 -0
- package/claude-assets/skills/workflow-codification/test-metadata-update.sh +350 -0
- package/claude-assets/skills/workflow-codification/track-cost-savings.sh +55 -14
- package/claude-assets/skills/workflow-codification/track-cost-savings.sh.backup-1763392821 +445 -0
- package/claude-assets/skills/workflow-codification/track-edge-case.sh +27 -60
- package/claude-assets/skills/workflow-codification/workflow-codification.db +0 -0
- package/dist/ace/ace-curator.js +10 -2
- package/dist/ace/ace-curator.js.map +1 -1
- package/dist/ace/ace-generator.js +4 -0
- package/dist/ace/ace-generator.js.map +1 -1
- package/dist/ace/ace-reflector.js +1 -1
- package/dist/ace/ace-reflector.js.map +1 -1
- package/dist/ace/context-injection.js +24 -2
- package/dist/ace/context-injection.js.map +1 -1
- package/dist/agents/agent-loader.js +146 -165
- package/dist/agents/agent-loader.js.map +1 -1
- package/dist/agents/task-agent-integration.js +1 -1
- package/dist/agents/task-agent-integration.js.map +1 -1
- package/dist/api/health-endpoints.js +390 -0
- package/dist/api/health-endpoints.js.map +1 -0
- package/dist/cli/agent-executor.js +4 -1
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/cli/agent-prompt-builder.js +89 -1
- package/dist/cli/agent-prompt-builder.js.map +1 -1
- package/dist/cli/agent-spawn.js +130 -37
- package/dist/cli/agent-spawn.js.map +1 -1
- package/dist/cli/skill-cache-validator.js +412 -0
- package/dist/cli/skill-cache-validator.js.map +1 -0
- package/dist/cli/skill-cli.js +991 -0
- package/dist/cli/skill-cli.js.map +1 -0
- package/dist/cli/skill-execution-logger.js +284 -0
- package/dist/cli/skill-execution-logger.js.map +1 -0
- package/dist/cli/skill-loader.js +457 -0
- package/dist/cli/skill-loader.js.map +1 -0
- package/dist/coordination/event-bus.js +2 -2
- package/dist/coordination/event-bus.js.map +1 -1
- package/dist/coordination/fleet-manager.js +1 -1
- package/dist/coordination/fleet-manager.js.map +1 -1
- package/dist/coordination/index.js +23 -9
- package/dist/coordination/index.js.map +1 -1
- package/dist/coordination/types/fleet-manager.types.js.map +1 -1
- package/dist/db/migration-manager.js +483 -0
- package/dist/db/migration-manager.js.map +1 -0
- package/dist/db/skills-query.js +535 -0
- package/dist/db/skills-query.js.map +1 -0
- package/dist/integration/DatabaseHandoff.js +1 -1
- package/dist/integration/DatabaseHandoff.js.map +1 -1
- package/dist/jobs/edge-case-analyzer.js +367 -0
- package/dist/jobs/edge-case-analyzer.js.map +1 -0
- package/dist/jobs/promotion-sla-enforcer.js +288 -0
- package/dist/jobs/promotion-sla-enforcer.js.map +1 -0
- package/dist/lib/agent-output-parser.js.map +1 -1
- package/dist/lib/agent-output-validator.js.map +1 -1
- package/dist/lib/agent-workspace.js +281 -0
- package/dist/lib/agent-workspace.js.map +1 -0
- package/dist/lib/atomic-file-writer.js +377 -0
- package/dist/lib/atomic-file-writer.js.map +1 -0
- package/dist/lib/backup-manager.js +779 -0
- package/dist/lib/backup-manager.js.map +1 -0
- package/dist/lib/checkpoint-manager.js +837 -0
- package/dist/lib/checkpoint-manager.js.map +1 -0
- package/dist/lib/circuit-breaker.js +340 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/completion-signal-handler.js +243 -0
- package/dist/lib/completion-signal-handler.js.map +1 -0
- package/dist/lib/config-manager.js +312 -0
- package/dist/lib/config-manager.js.map +1 -0
- package/dist/lib/config-migrator.js +386 -0
- package/dist/lib/config-migrator.js.map +1 -0
- package/dist/lib/config-validator.js.map +1 -1
- package/dist/lib/correlation-cache.js +311 -0
- package/dist/lib/correlation-cache.js.map +1 -0
- package/dist/lib/correlation.js +263 -0
- package/dist/lib/correlation.js.map +1 -0
- package/dist/lib/database-service/connection-pool-manager.js +520 -0
- package/dist/lib/database-service/connection-pool-manager.js.map +1 -0
- package/dist/lib/database-service/correlation.js +329 -0
- package/dist/lib/database-service/correlation.js.map +1 -0
- package/dist/lib/database-service/errors.js +120 -0
- package/dist/lib/database-service/errors.js.map +1 -0
- package/dist/lib/database-service/index.js +168 -0
- package/dist/lib/database-service/index.js.map +1 -0
- package/dist/lib/database-service/postgres-adapter.js +526 -0
- package/dist/lib/database-service/postgres-adapter.js.map +1 -0
- package/dist/lib/database-service/redis-adapter.js +360 -0
- package/dist/lib/database-service/redis-adapter.js.map +1 -0
- package/dist/lib/database-service/sqlite-adapter.js +544 -0
- package/dist/lib/database-service/sqlite-adapter.js.map +1 -0
- package/dist/lib/database-service/transaction-manager.js +773 -0
- package/dist/lib/database-service/transaction-manager.js.map +1 -0
- package/dist/lib/database-service/types.js +23 -0
- package/dist/lib/database-service/types.js.map +1 -0
- package/dist/lib/deadlock-resolver.js +292 -0
- package/dist/lib/deadlock-resolver.js.map +1 -0
- package/dist/lib/distributed-lock.js +451 -0
- package/dist/lib/distributed-lock.js.map +1 -0
- package/dist/lib/edge-case-deduplicator.js +227 -0
- package/dist/lib/edge-case-deduplicator.js.map +1 -0
- package/dist/lib/encryption-manager.js +322 -0
- package/dist/lib/encryption-manager.js.map +1 -0
- package/dist/lib/error-aggregator.js +234 -0
- package/dist/lib/error-aggregator.js.map +1 -0
- package/dist/lib/errors.js +287 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/file-lock-manager.js +578 -0
- package/dist/lib/file-lock-manager.js.map +1 -0
- package/dist/lib/file-operations.js +367 -0
- package/dist/lib/file-operations.js.map +1 -0
- package/dist/lib/idempotent-write.js +237 -0
- package/dist/lib/idempotent-write.js.map +1 -0
- package/dist/lib/integration-schema-validator.js +522 -0
- package/dist/lib/integration-schema-validator.js.map +1 -0
- package/dist/lib/lock-health-monitor.js +298 -0
- package/dist/lib/lock-health-monitor.js.map +1 -0
- package/dist/lib/log-shipper.js +422 -0
- package/dist/lib/log-shipper.js.map +1 -0
- package/dist/lib/logging.js +146 -0
- package/dist/lib/logging.js.map +1 -0
- package/dist/lib/message-deduplicator.js +439 -0
- package/dist/lib/message-deduplicator.js.map +1 -0
- package/dist/lib/multi-system-query.js +604 -0
- package/dist/lib/multi-system-query.js.map +1 -0
- package/dist/lib/orphan-detector.js +332 -0
- package/dist/lib/orphan-detector.js.map +1 -0
- package/dist/lib/password-generator.js +166 -0
- package/dist/lib/password-generator.js.map +1 -0
- package/dist/lib/path-validator.js +429 -0
- package/dist/lib/path-validator.js.map +1 -0
- package/dist/lib/query-translator.js +905 -0
- package/dist/lib/query-translator.js.map +1 -0
- package/dist/lib/queue-recovery.js +469 -0
- package/dist/lib/queue-recovery.js.map +1 -0
- package/dist/lib/redis-queue-manager.js +512 -0
- package/dist/lib/redis-queue-manager.js.map +1 -0
- package/dist/lib/reflection-archiver.js +272 -0
- package/dist/lib/reflection-archiver.js.map +1 -0
- package/dist/lib/retry-manager.js +453 -0
- package/dist/lib/retry-manager.js.map +1 -0
- package/dist/lib/retry.js +262 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/lib/schema-transform.js +695 -0
- package/dist/lib/schema-transform.js.map +1 -0
- package/dist/lib/schema-validator.js +491 -0
- package/dist/lib/schema-validator.js.map +1 -0
- package/dist/lib/skill-cache.js +297 -0
- package/dist/lib/skill-cache.js.map +1 -0
- package/dist/lib/skill-content-manager.js +337 -0
- package/dist/lib/skill-content-manager.js.map +1 -0
- package/dist/lib/skill-frontmatter-parser.js +237 -0
- package/dist/lib/skill-frontmatter-parser.js.map +1 -0
- package/dist/lib/skill-git-integration.js +275 -0
- package/dist/lib/skill-git-integration.js.map +1 -0
- package/dist/lib/skill-markdown-validator.js +396 -0
- package/dist/lib/skill-markdown-validator.js.map +1 -0
- package/dist/lib/skill-output-parser.js +312 -0
- package/dist/lib/skill-output-parser.js.map +1 -0
- package/dist/lib/unified-query-api.js +467 -0
- package/dist/lib/unified-query-api.js.map +1 -0
- package/dist/middleware/auth-middleware.js +350 -0
- package/dist/middleware/auth-middleware.js.map +1 -0
- package/dist/middleware/schema-validation.js +347 -0
- package/dist/middleware/schema-validation.js.map +1 -0
- package/dist/providers/anthropic-provider.js +1 -1
- package/dist/providers/anthropic-provider.js.map +1 -1
- package/dist/providers/provider-factory.js +2 -2
- package/dist/providers/provider-factory.js.map +1 -1
- package/dist/services/edge-case-analyzer.js +321 -0
- package/dist/services/edge-case-analyzer.js.map +1 -0
- package/dist/services/edge-case-deduplicator.js +266 -0
- package/dist/services/edge-case-deduplicator.js.map +1 -0
- package/dist/services/edge-case-detector.js +337 -0
- package/dist/services/edge-case-detector.js.map +1 -0
- package/dist/services/edge-case-tracker.js +547 -0
- package/dist/services/edge-case-tracker.js.map +1 -0
- package/dist/services/health-check-system.js +586 -0
- package/dist/services/health-check-system.js.map +1 -0
- package/dist/services/metrics-logger.js +412 -0
- package/dist/services/metrics-logger.js.map +1 -0
- package/dist/services/patch-generator.js +378 -0
- package/dist/services/patch-generator.js.map +1 -0
- package/dist/services/patch-validator.js +337 -0
- package/dist/services/patch-validator.js.map +1 -0
- package/dist/services/performance-monitor.js +811 -0
- package/dist/services/performance-monitor.js.map +1 -0
- package/dist/services/promotion-pipeline.js +918 -0
- package/dist/services/promotion-pipeline.js.map +1 -0
- package/dist/services/promotion-validator.js +394 -0
- package/dist/services/promotion-validator.js.map +1 -0
- package/dist/services/reflection-logger.js +388 -0
- package/dist/services/reflection-logger.js.map +1 -0
- package/dist/services/skill-deployment.js +472 -0
- package/dist/services/skill-deployment.js.map +1 -0
- package/dist/services/skill-loader.js +427 -0
- package/dist/services/skill-loader.js.map +1 -0
- package/dist/services/skill-promotion.js +372 -0
- package/dist/services/skill-promotion.js.map +1 -0
- package/dist/services/skill-validator.js +454 -0
- package/dist/services/skill-validator.js.map +1 -0
- package/dist/services/skill-versioning.js +244 -0
- package/dist/services/skill-versioning.js.map +1 -0
- package/dist/services/workspace-supervisor.js +597 -0
- package/dist/services/workspace-supervisor.js.map +1 -0
- package/dist/types/edge-case.js +45 -0
- package/dist/types/edge-case.js.map +1 -0
- package/package.json +201 -177
- package/readme/README.md +19 -4
- package/scripts/backup-cleanup.sh +627 -0
- package/scripts/cleanup-workspaces.sh +412 -0
- package/scripts/cleanup-yaml-configs.sh +141 -0
- package/scripts/deploy-approved-skills.sh +263 -0
- package/scripts/health-check.sh +447 -0
- package/scripts/log-aggregator.sh +554 -0
- package/scripts/log-monitor.sh +629 -0
- package/scripts/manage-agent-workspaces.sh +434 -0
- package/scripts/migrate-schema.sh +533 -0
- package/scripts/promote-staged-skills.sh +423 -0
- package/scripts/verify-no-secrets.sh +88 -35
- package/.claude/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
- package/.claude/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
- package/.claude/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
- package/.claude/skills/agent-lifecycle/SKILL.md +0 -60
- package/.claude/skills/agent-lifecycle/execute-lifecycle-hook.sh +0 -573
- package/.claude/skills/agent-lifecycle/simple-audit.sh +0 -31
- package/.claude/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
- package/README.md.backup_before_replace +0 -781
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
- package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log Shipper - Centralized Logging to Loki/Elasticsearch
|
|
3
|
+
*
|
|
4
|
+
* Provides high-performance log shipping to centralized logging stack with:
|
|
5
|
+
* - Batching and buffering for efficiency
|
|
6
|
+
* - Retry logic for resilience
|
|
7
|
+
* - Correlation ID propagation
|
|
8
|
+
* - JSON formatting
|
|
9
|
+
* - 30-day retention policies
|
|
10
|
+
* - Search query building
|
|
11
|
+
*
|
|
12
|
+
* Task P2-2.3: Centralized Logging with ELK/Loki Stack
|
|
13
|
+
* @version 1.0.0
|
|
14
|
+
*/ import * as fs from 'fs/promises';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
/**
|
|
17
|
+
* Log severity levels
|
|
18
|
+
*/ export var LogLevel = /*#__PURE__*/ function(LogLevel) {
|
|
19
|
+
LogLevel["DEBUG"] = "debug";
|
|
20
|
+
LogLevel["INFO"] = "info";
|
|
21
|
+
LogLevel["WARN"] = "warn";
|
|
22
|
+
LogLevel["ERROR"] = "error";
|
|
23
|
+
return LogLevel;
|
|
24
|
+
}({});
|
|
25
|
+
/**
|
|
26
|
+
* High-performance log shipper for centralized logging
|
|
27
|
+
*/ export class LogShipper {
|
|
28
|
+
lokiUrl;
|
|
29
|
+
elasticsearchUrl;
|
|
30
|
+
bufferSize;
|
|
31
|
+
flushInterval;
|
|
32
|
+
defaultLabels;
|
|
33
|
+
retryAttempts;
|
|
34
|
+
retryDelay;
|
|
35
|
+
persistDir;
|
|
36
|
+
buffer = [];
|
|
37
|
+
flushTimer;
|
|
38
|
+
metrics;
|
|
39
|
+
closed = false;
|
|
40
|
+
constructor(options = {}){
|
|
41
|
+
this.lokiUrl = options.lokiUrl || 'http://localhost:3100';
|
|
42
|
+
this.elasticsearchUrl = options.elasticsearchUrl;
|
|
43
|
+
this.bufferSize = options.bufferSize || 100;
|
|
44
|
+
this.flushInterval = options.flushInterval || 5000;
|
|
45
|
+
this.defaultLabels = options.defaultLabels || {
|
|
46
|
+
environment: 'production',
|
|
47
|
+
service: 'cfn'
|
|
48
|
+
};
|
|
49
|
+
this.retryAttempts = options.retryAttempts || 3;
|
|
50
|
+
this.retryDelay = options.retryDelay || 1000;
|
|
51
|
+
this.persistDir = options.persistDir || '/var/log/cfn/persist';
|
|
52
|
+
this.metrics = {
|
|
53
|
+
totalLogs: 0,
|
|
54
|
+
bufferedLogs: 0,
|
|
55
|
+
shippedLogs: 0,
|
|
56
|
+
failedLogs: 0,
|
|
57
|
+
errorCount: 0
|
|
58
|
+
};
|
|
59
|
+
// Start auto-flush timer
|
|
60
|
+
this.startAutoFlush();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Ship a log entry to centralized logging stack
|
|
64
|
+
*/ async ship(entry) {
|
|
65
|
+
if (this.closed) {
|
|
66
|
+
throw new Error('LogShipper is closed');
|
|
67
|
+
}
|
|
68
|
+
// Normalize entry
|
|
69
|
+
this.normalizeEntry(entry);
|
|
70
|
+
// Add to buffer
|
|
71
|
+
this.buffer.push(entry);
|
|
72
|
+
this.metrics.totalLogs++;
|
|
73
|
+
this.metrics.bufferedLogs++;
|
|
74
|
+
// Auto-flush if buffer is full
|
|
75
|
+
if (this.buffer.length >= this.bufferSize) {
|
|
76
|
+
await this.flush();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Flush buffered logs to centralized stack
|
|
81
|
+
*/ async flush() {
|
|
82
|
+
if (this.buffer.length === 0) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const logsToShip = [
|
|
86
|
+
...this.buffer
|
|
87
|
+
];
|
|
88
|
+
this.buffer = [];
|
|
89
|
+
this.metrics.bufferedLogs = 0;
|
|
90
|
+
try {
|
|
91
|
+
// Ship to Loki
|
|
92
|
+
await this.shipToLoki(logsToShip);
|
|
93
|
+
// Ship to Elasticsearch if configured
|
|
94
|
+
if (this.elasticsearchUrl) {
|
|
95
|
+
await this.shipToElasticsearch(logsToShip);
|
|
96
|
+
}
|
|
97
|
+
this.metrics.shippedLogs += logsToShip.length;
|
|
98
|
+
this.metrics.lastFlushTime = new Date().toISOString();
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.metrics.failedLogs += logsToShip.length;
|
|
101
|
+
this.metrics.errorCount++;
|
|
102
|
+
// Persist failed logs for retry
|
|
103
|
+
await this.persistLogs(logsToShip);
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Ship logs to Loki
|
|
109
|
+
*/ async shipToLoki(logs) {
|
|
110
|
+
const payload = this.formatLogsForLoki(logs);
|
|
111
|
+
const response = await this.retryWithBackoff(async ()=>{
|
|
112
|
+
return fetch(`${this.lokiUrl}/loki/api/v1/push`, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: {
|
|
115
|
+
'Content-Type': 'application/json'
|
|
116
|
+
},
|
|
117
|
+
body: JSON.stringify(payload)
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(`Loki API error: ${response.status} ${response.statusText}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Ship logs to Elasticsearch
|
|
126
|
+
*/ async shipToElasticsearch(logs) {
|
|
127
|
+
const bulkPayload = this.formatLogsForElasticsearch(logs);
|
|
128
|
+
const headers = {
|
|
129
|
+
'Content-Type': 'application/x-ndjson'
|
|
130
|
+
};
|
|
131
|
+
if (this.defaultLabels.username && this.defaultLabels.password) {
|
|
132
|
+
const auth = Buffer.from(`${this.defaultLabels.username}:${this.defaultLabels.password}`).toString('base64');
|
|
133
|
+
headers['Authorization'] = `Basic ${auth}`;
|
|
134
|
+
}
|
|
135
|
+
const response = await this.retryWithBackoff(async ()=>{
|
|
136
|
+
return fetch(`${this.elasticsearchUrl}/_bulk`, {
|
|
137
|
+
method: 'POST',
|
|
138
|
+
headers,
|
|
139
|
+
body: bulkPayload
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
if (!response.ok) {
|
|
143
|
+
throw new Error(`Elasticsearch API error: ${response.status} ${response.statusText}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Format logs for Loki push API
|
|
148
|
+
*/ formatLogsForLoki(logs) {
|
|
149
|
+
const streams = new Map();
|
|
150
|
+
for (const log of logs){
|
|
151
|
+
const labels = this.getLabelsForLog(log);
|
|
152
|
+
const labelStr = Object.entries(labels).map(([k, v])=>`${k}="${v}"`).join(', ');
|
|
153
|
+
if (!streams.has(labelStr)) {
|
|
154
|
+
streams.set(labelStr, []);
|
|
155
|
+
}
|
|
156
|
+
const timestamp = (new Date(log.timestamp).getTime() * 1000000).toString();
|
|
157
|
+
streams.get(labelStr).push([
|
|
158
|
+
timestamp,
|
|
159
|
+
this.formatLog(log)
|
|
160
|
+
]);
|
|
161
|
+
}
|
|
162
|
+
const streamsList = Array.from(streams.entries()).map(([labels, values])=>({
|
|
163
|
+
stream: this.parseLabels(labels),
|
|
164
|
+
values
|
|
165
|
+
}));
|
|
166
|
+
return {
|
|
167
|
+
streams: streamsList
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Format logs for Elasticsearch bulk API
|
|
172
|
+
*/ formatLogsForElasticsearch(logs) {
|
|
173
|
+
const lines = [];
|
|
174
|
+
const indexPrefix = `cfn-logs-${new Date().toISOString().split('T')[0]}`;
|
|
175
|
+
for (const log of logs){
|
|
176
|
+
// Index metadata
|
|
177
|
+
lines.push(JSON.stringify({
|
|
178
|
+
index: {
|
|
179
|
+
_index: indexPrefix,
|
|
180
|
+
_type: '_doc'
|
|
181
|
+
}
|
|
182
|
+
}));
|
|
183
|
+
// Document
|
|
184
|
+
lines.push(JSON.stringify(log));
|
|
185
|
+
}
|
|
186
|
+
return lines.join('\n') + '\n';
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get labels for a log entry
|
|
190
|
+
*/ getLabelsForLog(log) {
|
|
191
|
+
return {
|
|
192
|
+
...this.defaultLabels,
|
|
193
|
+
level: String(log.level),
|
|
194
|
+
service: log.context
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Parse label string into object
|
|
199
|
+
*/ parseLabels(labelStr) {
|
|
200
|
+
const labels = {};
|
|
201
|
+
const parts = labelStr.split(', ');
|
|
202
|
+
for (const part of parts){
|
|
203
|
+
const [key, value] = part.split('=');
|
|
204
|
+
labels[key] = value.replace(/^"|"$/g, '');
|
|
205
|
+
}
|
|
206
|
+
return labels;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Format log as JSON string
|
|
210
|
+
*/ formatLog(entry) {
|
|
211
|
+
const logObject = {
|
|
212
|
+
timestamp: entry.timestamp,
|
|
213
|
+
level: entry.level,
|
|
214
|
+
message: entry.message,
|
|
215
|
+
context: entry.context,
|
|
216
|
+
...entry.correlationId && {
|
|
217
|
+
correlationId: entry.correlationId
|
|
218
|
+
},
|
|
219
|
+
...entry.taskId && {
|
|
220
|
+
taskId: entry.taskId
|
|
221
|
+
},
|
|
222
|
+
...entry.agentId && {
|
|
223
|
+
agentId: entry.agentId
|
|
224
|
+
},
|
|
225
|
+
...entry.traceId && {
|
|
226
|
+
traceId: entry.traceId
|
|
227
|
+
},
|
|
228
|
+
...entry.metadata && {
|
|
229
|
+
metadata: entry.metadata
|
|
230
|
+
},
|
|
231
|
+
...entry.error && {
|
|
232
|
+
error: entry.error
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
return JSON.stringify(logObject);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Normalize log entry
|
|
239
|
+
*/ normalizeEntry(entry) {
|
|
240
|
+
// Ensure ISO 8601 timestamp
|
|
241
|
+
if (!entry.timestamp) {
|
|
242
|
+
entry.timestamp = new Date().toISOString();
|
|
243
|
+
}
|
|
244
|
+
// Validate timestamp format
|
|
245
|
+
if (!/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(entry.timestamp)) {
|
|
246
|
+
try {
|
|
247
|
+
entry.timestamp = new Date(entry.timestamp).toISOString();
|
|
248
|
+
} catch {
|
|
249
|
+
entry.timestamp = new Date().toISOString();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Ensure level is string
|
|
253
|
+
entry.level = String(entry.level).toLowerCase();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Check if log entry has expired based on retention policy
|
|
257
|
+
*/ isLogExpired(entry, retentionDays) {
|
|
258
|
+
try {
|
|
259
|
+
const logTime = new Date(entry.timestamp).getTime();
|
|
260
|
+
const now = new Date().getTime();
|
|
261
|
+
const retentionMs = retentionDays * 24 * 60 * 60 * 1000;
|
|
262
|
+
return now - logTime > retentionMs;
|
|
263
|
+
} catch {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Clean up expired logs
|
|
269
|
+
*/ async cleanupExpiredLogs(retentionDays) {
|
|
270
|
+
let cleanedCount = 0;
|
|
271
|
+
try {
|
|
272
|
+
// Clean from buffer
|
|
273
|
+
const beforeCount = this.buffer.length;
|
|
274
|
+
this.buffer = this.buffer.filter((log)=>!this.isLogExpired(log, retentionDays));
|
|
275
|
+
cleanedCount += beforeCount - this.buffer.length;
|
|
276
|
+
// Clean from persistent storage
|
|
277
|
+
const persisted = await this.getPersistentLogs();
|
|
278
|
+
for (const log of persisted){
|
|
279
|
+
if (this.isLogExpired(log, retentionDays)) {
|
|
280
|
+
cleanedCount++;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error('Error cleaning up expired logs:', error);
|
|
285
|
+
}
|
|
286
|
+
return cleanedCount;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Build LogQL query string
|
|
290
|
+
*/ buildLogQLQuery(options) {
|
|
291
|
+
let query = '{';
|
|
292
|
+
if (options.level) {
|
|
293
|
+
query += `level="${options.level}",`;
|
|
294
|
+
}
|
|
295
|
+
if (options.service) {
|
|
296
|
+
query += `service="${options.service}",`;
|
|
297
|
+
}
|
|
298
|
+
query = query.replace(/,$/g, '}');
|
|
299
|
+
if (options.correlationId) {
|
|
300
|
+
query += ` | json correlationId="${options.correlationId}"`;
|
|
301
|
+
}
|
|
302
|
+
if (options.timeRange) {
|
|
303
|
+
const start = Math.floor(options.timeRange.start.getTime() / 1000);
|
|
304
|
+
const end = Math.floor(options.timeRange.end.getTime() / 1000);
|
|
305
|
+
query = `${start}s,${end}s ${query}`;
|
|
306
|
+
}
|
|
307
|
+
return query;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Get metrics for dashboard integration
|
|
311
|
+
*/ getMetrics() {
|
|
312
|
+
return {
|
|
313
|
+
...this.metrics,
|
|
314
|
+
bufferedLogs: this.buffer.length
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Calculate error rate
|
|
319
|
+
*/ getErrorRate() {
|
|
320
|
+
if (this.metrics.totalLogs === 0) {
|
|
321
|
+
return 0;
|
|
322
|
+
}
|
|
323
|
+
const errorLogs = this.buffer.filter((log)=>log.level === "error").length;
|
|
324
|
+
return errorLogs / this.metrics.totalLogs;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Get persisted logs from disk
|
|
328
|
+
*/ async getPersistentLogs() {
|
|
329
|
+
const logs = [];
|
|
330
|
+
try {
|
|
331
|
+
const files = await fs.readdir(this.persistDir);
|
|
332
|
+
for (const file of files){
|
|
333
|
+
try {
|
|
334
|
+
const filePath = path.join(this.persistDir, file);
|
|
335
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
336
|
+
const logEntry = JSON.parse(content);
|
|
337
|
+
logs.push(logEntry);
|
|
338
|
+
} catch {
|
|
339
|
+
// Skip invalid files
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
} catch {
|
|
343
|
+
// Persist dir doesn't exist yet
|
|
344
|
+
}
|
|
345
|
+
return logs;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Persist logs to disk for retry on failure
|
|
349
|
+
*/ async persistLogs(logs) {
|
|
350
|
+
try {
|
|
351
|
+
await fs.mkdir(this.persistDir, {
|
|
352
|
+
recursive: true
|
|
353
|
+
});
|
|
354
|
+
for (const log of logs){
|
|
355
|
+
const filename = `${Date.now()}-${Math.random().toString(36).slice(2)}.json`;
|
|
356
|
+
const filePath = path.join(this.persistDir, filename);
|
|
357
|
+
await fs.writeFile(filePath, JSON.stringify(log));
|
|
358
|
+
}
|
|
359
|
+
} catch (error) {
|
|
360
|
+
console.error('Error persisting logs:', error);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Retry with exponential backoff
|
|
365
|
+
*/ async retryWithBackoff(fn, attempt = 0) {
|
|
366
|
+
try {
|
|
367
|
+
return await fn();
|
|
368
|
+
} catch (error) {
|
|
369
|
+
if (attempt < this.retryAttempts) {
|
|
370
|
+
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
371
|
+
await new Promise((resolve)=>setTimeout(resolve, delay));
|
|
372
|
+
return this.retryWithBackoff(fn, attempt + 1);
|
|
373
|
+
}
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Start auto-flush timer
|
|
379
|
+
*/ startAutoFlush() {
|
|
380
|
+
this.flushTimer = setInterval(async ()=>{
|
|
381
|
+
if (this.buffer.length > 0 && !this.closed) {
|
|
382
|
+
try {
|
|
383
|
+
await this.flush();
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.error('Auto-flush error:', error);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}, this.flushInterval);
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Close the shipper and clean up resources
|
|
392
|
+
*/ async close() {
|
|
393
|
+
this.closed = true;
|
|
394
|
+
if (this.flushTimer) {
|
|
395
|
+
clearInterval(this.flushTimer);
|
|
396
|
+
}
|
|
397
|
+
// Flush remaining logs
|
|
398
|
+
if (this.buffer.length > 0) {
|
|
399
|
+
try {
|
|
400
|
+
await this.flush();
|
|
401
|
+
} catch (error) {
|
|
402
|
+
console.error('Error flushing remaining logs:', error);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Getter methods for configuration
|
|
408
|
+
*/ getLokiUrl() {
|
|
409
|
+
return this.lokiUrl;
|
|
410
|
+
}
|
|
411
|
+
getDefaultLabels() {
|
|
412
|
+
return this.defaultLabels;
|
|
413
|
+
}
|
|
414
|
+
getBufferSize() {
|
|
415
|
+
return this.bufferSize;
|
|
416
|
+
}
|
|
417
|
+
getFlushInterval() {
|
|
418
|
+
return this.flushInterval;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
//# sourceMappingURL=log-shipper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/log-shipper.ts"],"sourcesContent":["/**\r\n * Log Shipper - Centralized Logging to Loki/Elasticsearch\r\n *\r\n * Provides high-performance log shipping to centralized logging stack with:\r\n * - Batching and buffering for efficiency\r\n * - Retry logic for resilience\r\n * - Correlation ID propagation\r\n * - JSON formatting\r\n * - 30-day retention policies\r\n * - Search query building\r\n *\r\n * Task P2-2.3: Centralized Logging with ELK/Loki Stack\r\n * @version 1.0.0\r\n */\r\n\r\nimport * as fs from 'fs/promises';\r\nimport * as path from 'path';\r\n\r\n/**\r\n * Log severity levels\r\n */\r\nexport enum LogLevel {\r\n DEBUG = 'debug',\r\n INFO = 'info',\r\n WARN = 'warn',\r\n ERROR = 'error',\r\n}\r\n\r\n/**\r\n * Structured log entry\r\n */\r\nexport interface LogEntry {\r\n timestamp: string; // ISO 8601 format\r\n level: LogLevel | string;\r\n message: string;\r\n context: string; // Service/source name\r\n correlationId?: string;\r\n taskId?: string;\r\n agentId?: string;\r\n traceId?: string;\r\n metadata?: Record<string, any>;\r\n error?: {\r\n name: string;\r\n message: string;\r\n stack?: string;\r\n };\r\n source?: string;\r\n}\r\n\r\n/**\r\n * Log shipping options\r\n */\r\nexport interface ShippingOptions {\r\n lokiUrl?: string;\r\n bufferSize?: number;\r\n flushInterval?: number;\r\n defaultLabels?: Record<string, string>;\r\n retryAttempts?: number;\r\n retryDelay?: number;\r\n persistDir?: string;\r\n elasticsearchUrl?: string;\r\n username?: string;\r\n password?: string;\r\n}\r\n\r\n/**\r\n * Query options for log search\r\n */\r\nexport interface QueryOptions {\r\n level?: string;\r\n service?: string;\r\n correlationId?: string;\r\n timeRange?: {\r\n start: Date;\r\n end: Date;\r\n };\r\n}\r\n\r\n/**\r\n * Metrics for dashboard integration\r\n */\r\nexport interface ShipperMetrics {\r\n totalLogs: number;\r\n bufferedLogs: number;\r\n shippedLogs: number;\r\n failedLogs: number;\r\n lastFlushTime?: string;\r\n errorCount: number;\r\n}\r\n\r\n/**\r\n * High-performance log shipper for centralized logging\r\n */\r\nexport class LogShipper {\r\n private lokiUrl: string;\r\n private elasticsearchUrl?: string;\r\n private bufferSize: number;\r\n private flushInterval: number;\r\n private defaultLabels: Record<string, string>;\r\n private retryAttempts: number;\r\n private retryDelay: number;\r\n private persistDir: string;\r\n\r\n private buffer: LogEntry[] = [];\r\n private flushTimer?: NodeJS.Timeout;\r\n private metrics: ShipperMetrics;\r\n private closed = false;\r\n\r\n constructor(options: ShippingOptions = {}) {\r\n this.lokiUrl = options.lokiUrl || 'http://localhost:3100';\r\n this.elasticsearchUrl = options.elasticsearchUrl;\r\n this.bufferSize = options.bufferSize || 100;\r\n this.flushInterval = options.flushInterval || 5000;\r\n this.defaultLabels = options.defaultLabels || { environment: 'production', service: 'cfn' };\r\n this.retryAttempts = options.retryAttempts || 3;\r\n this.retryDelay = options.retryDelay || 1000;\r\n this.persistDir = options.persistDir || '/var/log/cfn/persist';\r\n\r\n this.metrics = {\r\n totalLogs: 0,\r\n bufferedLogs: 0,\r\n shippedLogs: 0,\r\n failedLogs: 0,\r\n errorCount: 0,\r\n };\r\n\r\n // Start auto-flush timer\r\n this.startAutoFlush();\r\n }\r\n\r\n /**\r\n * Ship a log entry to centralized logging stack\r\n */\r\n async ship(entry: LogEntry): Promise<void> {\r\n if (this.closed) {\r\n throw new Error('LogShipper is closed');\r\n }\r\n\r\n // Normalize entry\r\n this.normalizeEntry(entry);\r\n\r\n // Add to buffer\r\n this.buffer.push(entry);\r\n this.metrics.totalLogs++;\r\n this.metrics.bufferedLogs++;\r\n\r\n // Auto-flush if buffer is full\r\n if (this.buffer.length >= this.bufferSize) {\r\n await this.flush();\r\n }\r\n }\r\n\r\n /**\r\n * Flush buffered logs to centralized stack\r\n */\r\n async flush(): Promise<void> {\r\n if (this.buffer.length === 0) {\r\n return;\r\n }\r\n\r\n const logsToShip = [...this.buffer];\r\n this.buffer = [];\r\n this.metrics.bufferedLogs = 0;\r\n\r\n try {\r\n // Ship to Loki\r\n await this.shipToLoki(logsToShip);\r\n\r\n // Ship to Elasticsearch if configured\r\n if (this.elasticsearchUrl) {\r\n await this.shipToElasticsearch(logsToShip);\r\n }\r\n\r\n this.metrics.shippedLogs += logsToShip.length;\r\n this.metrics.lastFlushTime = new Date().toISOString();\r\n } catch (error) {\r\n this.metrics.failedLogs += logsToShip.length;\r\n this.metrics.errorCount++;\r\n\r\n // Persist failed logs for retry\r\n await this.persistLogs(logsToShip);\r\n\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Ship logs to Loki\r\n */\r\n private async shipToLoki(logs: LogEntry[]): Promise<void> {\r\n const payload = this.formatLogsForLoki(logs);\r\n\r\n const response = await this.retryWithBackoff(async () => {\r\n return fetch(`${this.lokiUrl}/loki/api/v1/push`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify(payload),\r\n });\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Loki API error: ${response.status} ${response.statusText}`);\r\n }\r\n }\r\n\r\n /**\r\n * Ship logs to Elasticsearch\r\n */\r\n private async shipToElasticsearch(logs: LogEntry[]): Promise<void> {\r\n const bulkPayload = this.formatLogsForElasticsearch(logs);\r\n\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/x-ndjson',\r\n };\r\n\r\n if (this.defaultLabels.username && this.defaultLabels.password) {\r\n const auth = Buffer.from(\r\n `${this.defaultLabels.username}:${this.defaultLabels.password}`\r\n ).toString('base64');\r\n headers['Authorization'] = `Basic ${auth}`;\r\n }\r\n\r\n const response = await this.retryWithBackoff(async () => {\r\n return fetch(`${this.elasticsearchUrl}/_bulk`, {\r\n method: 'POST',\r\n headers,\r\n body: bulkPayload,\r\n });\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Elasticsearch API error: ${response.status} ${response.statusText}`);\r\n }\r\n }\r\n\r\n /**\r\n * Format logs for Loki push API\r\n */\r\n private formatLogsForLoki(logs: LogEntry[]): any {\r\n const streams = new Map<string, any[]>();\r\n\r\n for (const log of logs) {\r\n const labels = this.getLabelsForLog(log);\r\n const labelStr = Object.entries(labels)\r\n .map(([k, v]) => `${k}=\"${v}\"`)\r\n .join(', ');\r\n\r\n if (!streams.has(labelStr)) {\r\n streams.set(labelStr, []);\r\n }\r\n\r\n const timestamp = (new Date(log.timestamp).getTime() * 1000000).toString();\r\n streams.get(labelStr)!.push([timestamp, this.formatLog(log)]);\r\n }\r\n\r\n const streamsList = Array.from(streams.entries()).map(([labels, values]) => ({\r\n stream: this.parseLabels(labels),\r\n values,\r\n }));\r\n\r\n return { streams: streamsList };\r\n }\r\n\r\n /**\r\n * Format logs for Elasticsearch bulk API\r\n */\r\n private formatLogsForElasticsearch(logs: LogEntry[]): string {\r\n const lines: string[] = [];\r\n const indexPrefix = `cfn-logs-${new Date().toISOString().split('T')[0]}`;\r\n\r\n for (const log of logs) {\r\n // Index metadata\r\n lines.push(\r\n JSON.stringify({\r\n index: {\r\n _index: indexPrefix,\r\n _type: '_doc',\r\n },\r\n })\r\n );\r\n\r\n // Document\r\n lines.push(JSON.stringify(log));\r\n }\r\n\r\n return lines.join('\\n') + '\\n';\r\n }\r\n\r\n /**\r\n * Get labels for a log entry\r\n */\r\n private getLabelsForLog(log: LogEntry): Record<string, string> {\r\n return {\r\n ...this.defaultLabels,\r\n level: String(log.level),\r\n service: log.context,\r\n };\r\n }\r\n\r\n /**\r\n * Parse label string into object\r\n */\r\n private parseLabels(labelStr: string): Record<string, string> {\r\n const labels: Record<string, string> = {};\r\n const parts = labelStr.split(', ');\r\n\r\n for (const part of parts) {\r\n const [key, value] = part.split('=');\r\n labels[key] = value.replace(/^\"|\"$/g, '');\r\n }\r\n\r\n return labels;\r\n }\r\n\r\n /**\r\n * Format log as JSON string\r\n */\r\n formatLog(entry: LogEntry): string {\r\n const logObject = {\r\n timestamp: entry.timestamp,\r\n level: entry.level,\r\n message: entry.message,\r\n context: entry.context,\r\n ...(entry.correlationId && { correlationId: entry.correlationId }),\r\n ...(entry.taskId && { taskId: entry.taskId }),\r\n ...(entry.agentId && { agentId: entry.agentId }),\r\n ...(entry.traceId && { traceId: entry.traceId }),\r\n ...(entry.metadata && { metadata: entry.metadata }),\r\n ...(entry.error && { error: entry.error }),\r\n };\r\n\r\n return JSON.stringify(logObject);\r\n }\r\n\r\n /**\r\n * Normalize log entry\r\n */\r\n private normalizeEntry(entry: LogEntry): void {\r\n // Ensure ISO 8601 timestamp\r\n if (!entry.timestamp) {\r\n entry.timestamp = new Date().toISOString();\r\n }\r\n\r\n // Validate timestamp format\r\n if (!/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(entry.timestamp)) {\r\n try {\r\n entry.timestamp = new Date(entry.timestamp).toISOString();\r\n } catch {\r\n entry.timestamp = new Date().toISOString();\r\n }\r\n }\r\n\r\n // Ensure level is string\r\n entry.level = String(entry.level).toLowerCase();\r\n }\r\n\r\n /**\r\n * Check if log entry has expired based on retention policy\r\n */\r\n isLogExpired(entry: LogEntry, retentionDays: number): boolean {\r\n try {\r\n const logTime = new Date(entry.timestamp).getTime();\r\n const now = new Date().getTime();\r\n const retentionMs = retentionDays * 24 * 60 * 60 * 1000;\r\n\r\n return now - logTime > retentionMs;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Clean up expired logs\r\n */\r\n async cleanupExpiredLogs(retentionDays: number): Promise<number> {\r\n let cleanedCount = 0;\r\n\r\n try {\r\n // Clean from buffer\r\n const beforeCount = this.buffer.length;\r\n this.buffer = this.buffer.filter(log => !this.isLogExpired(log, retentionDays));\r\n cleanedCount += beforeCount - this.buffer.length;\r\n\r\n // Clean from persistent storage\r\n const persisted = await this.getPersistentLogs();\r\n for (const log of persisted) {\r\n if (this.isLogExpired(log, retentionDays)) {\r\n cleanedCount++;\r\n }\r\n }\r\n } catch (error) {\r\n console.error('Error cleaning up expired logs:', error);\r\n }\r\n\r\n return cleanedCount;\r\n }\r\n\r\n /**\r\n * Build LogQL query string\r\n */\r\n buildLogQLQuery(options: QueryOptions): string {\r\n let query = '{';\r\n\r\n if (options.level) {\r\n query += `level=\"${options.level}\",`;\r\n }\r\n\r\n if (options.service) {\r\n query += `service=\"${options.service}\",`;\r\n }\r\n\r\n query = query.replace(/,$/g, '}');\r\n\r\n if (options.correlationId) {\r\n query += ` | json correlationId=\"${options.correlationId}\"`;\r\n }\r\n\r\n if (options.timeRange) {\r\n const start = Math.floor(options.timeRange.start.getTime() / 1000);\r\n const end = Math.floor(options.timeRange.end.getTime() / 1000);\r\n query = `${start}s,${end}s ${query}`;\r\n }\r\n\r\n return query;\r\n }\r\n\r\n /**\r\n * Get metrics for dashboard integration\r\n */\r\n getMetrics(): ShipperMetrics {\r\n return {\r\n ...this.metrics,\r\n bufferedLogs: this.buffer.length,\r\n };\r\n }\r\n\r\n /**\r\n * Calculate error rate\r\n */\r\n getErrorRate(): number {\r\n if (this.metrics.totalLogs === 0) {\r\n return 0;\r\n }\r\n\r\n const errorLogs = this.buffer.filter(log => log.level === LogLevel.ERROR).length;\r\n return errorLogs / this.metrics.totalLogs;\r\n }\r\n\r\n /**\r\n * Get persisted logs from disk\r\n */\r\n async getPersistentLogs(): Promise<LogEntry[]> {\r\n const logs: LogEntry[] = [];\r\n\r\n try {\r\n const files = await fs.readdir(this.persistDir);\r\n\r\n for (const file of files) {\r\n try {\r\n const filePath = path.join(this.persistDir, file);\r\n const content = await fs.readFile(filePath, 'utf-8');\r\n const logEntry = JSON.parse(content);\r\n logs.push(logEntry);\r\n } catch {\r\n // Skip invalid files\r\n }\r\n }\r\n } catch {\r\n // Persist dir doesn't exist yet\r\n }\r\n\r\n return logs;\r\n }\r\n\r\n /**\r\n * Persist logs to disk for retry on failure\r\n */\r\n private async persistLogs(logs: LogEntry[]): Promise<void> {\r\n try {\r\n await fs.mkdir(this.persistDir, { recursive: true });\r\n\r\n for (const log of logs) {\r\n const filename = `${Date.now()}-${Math.random().toString(36).slice(2)}.json`;\r\n const filePath = path.join(this.persistDir, filename);\r\n await fs.writeFile(filePath, JSON.stringify(log));\r\n }\r\n } catch (error) {\r\n console.error('Error persisting logs:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Retry with exponential backoff\r\n */\r\n private async retryWithBackoff<T>(\r\n fn: () => Promise<T>,\r\n attempt = 0\r\n ): Promise<T> {\r\n try {\r\n return await fn();\r\n } catch (error) {\r\n if (attempt < this.retryAttempts) {\r\n const delay = this.retryDelay * Math.pow(2, attempt);\r\n await new Promise(resolve => setTimeout(resolve, delay));\r\n return this.retryWithBackoff(fn, attempt + 1);\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Start auto-flush timer\r\n */\r\n private startAutoFlush(): void {\r\n this.flushTimer = setInterval(async () => {\r\n if (this.buffer.length > 0 && !this.closed) {\r\n try {\r\n await this.flush();\r\n } catch (error) {\r\n console.error('Auto-flush error:', error);\r\n }\r\n }\r\n }, this.flushInterval);\r\n }\r\n\r\n /**\r\n * Close the shipper and clean up resources\r\n */\r\n async close(): Promise<void> {\r\n this.closed = true;\r\n\r\n if (this.flushTimer) {\r\n clearInterval(this.flushTimer);\r\n }\r\n\r\n // Flush remaining logs\r\n if (this.buffer.length > 0) {\r\n try {\r\n await this.flush();\r\n } catch (error) {\r\n console.error('Error flushing remaining logs:', error);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Getter methods for configuration\r\n */\r\n getLokiUrl(): string {\r\n return this.lokiUrl;\r\n }\r\n\r\n getDefaultLabels(): Record<string, string> {\r\n return this.defaultLabels;\r\n }\r\n\r\n getBufferSize(): number {\r\n return this.bufferSize;\r\n }\r\n\r\n getFlushInterval(): number {\r\n return this.flushInterval;\r\n }\r\n}\r\n"],"names":["fs","path","LogLevel","LogShipper","lokiUrl","elasticsearchUrl","bufferSize","flushInterval","defaultLabels","retryAttempts","retryDelay","persistDir","buffer","flushTimer","metrics","closed","options","environment","service","totalLogs","bufferedLogs","shippedLogs","failedLogs","errorCount","startAutoFlush","ship","entry","Error","normalizeEntry","push","length","flush","logsToShip","shipToLoki","shipToElasticsearch","lastFlushTime","Date","toISOString","error","persistLogs","logs","payload","formatLogsForLoki","response","retryWithBackoff","fetch","method","headers","body","JSON","stringify","ok","status","statusText","bulkPayload","formatLogsForElasticsearch","username","password","auth","Buffer","from","toString","streams","Map","log","labels","getLabelsForLog","labelStr","Object","entries","map","k","v","join","has","set","timestamp","getTime","get","formatLog","streamsList","Array","values","stream","parseLabels","lines","indexPrefix","split","index","_index","_type","level","String","context","parts","part","key","value","replace","logObject","message","correlationId","taskId","agentId","traceId","metadata","test","toLowerCase","isLogExpired","retentionDays","logTime","now","retentionMs","cleanupExpiredLogs","cleanedCount","beforeCount","filter","persisted","getPersistentLogs","console","buildLogQLQuery","query","timeRange","start","Math","floor","end","getMetrics","getErrorRate","errorLogs","files","readdir","file","filePath","content","readFile","logEntry","parse","mkdir","recursive","filename","random","slice","writeFile","fn","attempt","delay","pow","Promise","resolve","setTimeout","setInterval","close","clearInterval","getLokiUrl","getDefaultLabels","getBufferSize","getFlushInterval"],"mappings":"AAAA;;;;;;;;;;;;;CAaC,GAED,YAAYA,QAAQ,cAAc;AAClC,YAAYC,UAAU,OAAO;AAE7B;;CAEC,GACD,OAAO,IAAA,AAAKC,kCAAAA;;;;;WAAAA;MAKX;AAgED;;CAEC,GACD,OAAO,MAAMC;IACHC,QAAgB;IAChBC,iBAA0B;IAC1BC,WAAmB;IACnBC,cAAsB;IACtBC,cAAsC;IACtCC,cAAsB;IACtBC,WAAmB;IACnBC,WAAmB;IAEnBC,SAAqB,EAAE,CAAC;IACxBC,WAA4B;IAC5BC,QAAwB;IACxBC,SAAS,MAAM;IAEvB,YAAYC,UAA2B,CAAC,CAAC,CAAE;QACzC,IAAI,CAACZ,OAAO,GAAGY,QAAQZ,OAAO,IAAI;QAClC,IAAI,CAACC,gBAAgB,GAAGW,QAAQX,gBAAgB;QAChD,IAAI,CAACC,UAAU,GAAGU,QAAQV,UAAU,IAAI;QACxC,IAAI,CAACC,aAAa,GAAGS,QAAQT,aAAa,IAAI;QAC9C,IAAI,CAACC,aAAa,GAAGQ,QAAQR,aAAa,IAAI;YAAES,aAAa;YAAcC,SAAS;QAAM;QAC1F,IAAI,CAACT,aAAa,GAAGO,QAAQP,aAAa,IAAI;QAC9C,IAAI,CAACC,UAAU,GAAGM,QAAQN,UAAU,IAAI;QACxC,IAAI,CAACC,UAAU,GAAGK,QAAQL,UAAU,IAAI;QAExC,IAAI,CAACG,OAAO,GAAG;YACbK,WAAW;YACXC,cAAc;YACdC,aAAa;YACbC,YAAY;YACZC,YAAY;QACd;QAEA,yBAAyB;QACzB,IAAI,CAACC,cAAc;IACrB;IAEA;;GAEC,GACD,MAAMC,KAAKC,KAAe,EAAiB;QACzC,IAAI,IAAI,CAACX,MAAM,EAAE;YACf,MAAM,IAAIY,MAAM;QAClB;QAEA,kBAAkB;QAClB,IAAI,CAACC,cAAc,CAACF;QAEpB,gBAAgB;QAChB,IAAI,CAACd,MAAM,CAACiB,IAAI,CAACH;QACjB,IAAI,CAACZ,OAAO,CAACK,SAAS;QACtB,IAAI,CAACL,OAAO,CAACM,YAAY;QAEzB,+BAA+B;QAC/B,IAAI,IAAI,CAACR,MAAM,CAACkB,MAAM,IAAI,IAAI,CAACxB,UAAU,EAAE;YACzC,MAAM,IAAI,CAACyB,KAAK;QAClB;IACF;IAEA;;GAEC,GACD,MAAMA,QAAuB;QAC3B,IAAI,IAAI,CAACnB,MAAM,CAACkB,MAAM,KAAK,GAAG;YAC5B;QACF;QAEA,MAAME,aAAa;eAAI,IAAI,CAACpB,MAAM;SAAC;QACnC,IAAI,CAACA,MAAM,GAAG,EAAE;QAChB,IAAI,CAACE,OAAO,CAACM,YAAY,GAAG;QAE5B,IAAI;YACF,eAAe;YACf,MAAM,IAAI,CAACa,UAAU,CAACD;YAEtB,sCAAsC;YACtC,IAAI,IAAI,CAAC3B,gBAAgB,EAAE;gBACzB,MAAM,IAAI,CAAC6B,mBAAmB,CAACF;YACjC;YAEA,IAAI,CAAClB,OAAO,CAACO,WAAW,IAAIW,WAAWF,MAAM;YAC7C,IAAI,CAAChB,OAAO,CAACqB,aAAa,GAAG,IAAIC,OAAOC,WAAW;QACrD,EAAE,OAAOC,OAAO;YACd,IAAI,CAACxB,OAAO,CAACQ,UAAU,IAAIU,WAAWF,MAAM;YAC5C,IAAI,CAAChB,OAAO,CAACS,UAAU;YAEvB,gCAAgC;YAChC,MAAM,IAAI,CAACgB,WAAW,CAACP;YAEvB,MAAMM;QACR;IACF;IAEA;;GAEC,GACD,MAAcL,WAAWO,IAAgB,EAAiB;QACxD,MAAMC,UAAU,IAAI,CAACC,iBAAiB,CAACF;QAEvC,MAAMG,WAAW,MAAM,IAAI,CAACC,gBAAgB,CAAC;YAC3C,OAAOC,MAAM,GAAG,IAAI,CAACzC,OAAO,CAAC,iBAAiB,CAAC,EAAE;gBAC/C0C,QAAQ;gBACRC,SAAS;oBACP,gBAAgB;gBAClB;gBACAC,MAAMC,KAAKC,SAAS,CAACT;YACvB;QACF;QAEA,IAAI,CAACE,SAASQ,EAAE,EAAE;YAChB,MAAM,IAAIxB,MAAM,CAAC,gBAAgB,EAAEgB,SAASS,MAAM,CAAC,CAAC,EAAET,SAASU,UAAU,EAAE;QAC7E;IACF;IAEA;;GAEC,GACD,MAAcnB,oBAAoBM,IAAgB,EAAiB;QACjE,MAAMc,cAAc,IAAI,CAACC,0BAA0B,CAACf;QAEpD,MAAMO,UAAkC;YACtC,gBAAgB;QAClB;QAEA,IAAI,IAAI,CAACvC,aAAa,CAACgD,QAAQ,IAAI,IAAI,CAAChD,aAAa,CAACiD,QAAQ,EAAE;YAC9D,MAAMC,OAAOC,OAAOC,IAAI,CACtB,GAAG,IAAI,CAACpD,aAAa,CAACgD,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAChD,aAAa,CAACiD,QAAQ,EAAE,EAC/DI,QAAQ,CAAC;YACXd,OAAO,CAAC,gBAAgB,GAAG,CAAC,MAAM,EAAEW,MAAM;QAC5C;QAEA,MAAMf,WAAW,MAAM,IAAI,CAACC,gBAAgB,CAAC;YAC3C,OAAOC,MAAM,GAAG,IAAI,CAACxC,gBAAgB,CAAC,MAAM,CAAC,EAAE;gBAC7CyC,QAAQ;gBACRC;gBACAC,MAAMM;YACR;QACF;QAEA,IAAI,CAACX,SAASQ,EAAE,EAAE;YAChB,MAAM,IAAIxB,MAAM,CAAC,yBAAyB,EAAEgB,SAASS,MAAM,CAAC,CAAC,EAAET,SAASU,UAAU,EAAE;QACtF;IACF;IAEA;;GAEC,GACD,AAAQX,kBAAkBF,IAAgB,EAAO;QAC/C,MAAMsB,UAAU,IAAIC;QAEpB,KAAK,MAAMC,OAAOxB,KAAM;YACtB,MAAMyB,SAAS,IAAI,CAACC,eAAe,CAACF;YACpC,MAAMG,WAAWC,OAAOC,OAAO,CAACJ,QAC7BK,GAAG,CAAC,CAAC,CAACC,GAAGC,EAAE,GAAK,GAAGD,EAAE,EAAE,EAAEC,EAAE,CAAC,CAAC,EAC7BC,IAAI,CAAC;YAER,IAAI,CAACX,QAAQY,GAAG,CAACP,WAAW;gBAC1BL,QAAQa,GAAG,CAACR,UAAU,EAAE;YAC1B;YAEA,MAAMS,YAAY,AAAC,CAAA,IAAIxC,KAAK4B,IAAIY,SAAS,EAAEC,OAAO,KAAK,OAAM,EAAGhB,QAAQ;YACxEC,QAAQgB,GAAG,CAACX,UAAWtC,IAAI,CAAC;gBAAC+C;gBAAW,IAAI,CAACG,SAAS,CAACf;aAAK;QAC9D;QAEA,MAAMgB,cAAcC,MAAMrB,IAAI,CAACE,QAAQO,OAAO,IAAIC,GAAG,CAAC,CAAC,CAACL,QAAQiB,OAAO,GAAM,CAAA;gBAC3EC,QAAQ,IAAI,CAACC,WAAW,CAACnB;gBACzBiB;YACF,CAAA;QAEA,OAAO;YAAEpB,SAASkB;QAAY;IAChC;IAEA;;GAEC,GACD,AAAQzB,2BAA2Bf,IAAgB,EAAU;QAC3D,MAAM6C,QAAkB,EAAE;QAC1B,MAAMC,cAAc,CAAC,SAAS,EAAE,IAAIlD,OAAOC,WAAW,GAAGkD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE;QAExE,KAAK,MAAMvB,OAAOxB,KAAM;YACtB,iBAAiB;YACjB6C,MAAMxD,IAAI,CACRoB,KAAKC,SAAS,CAAC;gBACbsC,OAAO;oBACLC,QAAQH;oBACRI,OAAO;gBACT;YACF;YAGF,WAAW;YACXL,MAAMxD,IAAI,CAACoB,KAAKC,SAAS,CAACc;QAC5B;QAEA,OAAOqB,MAAMZ,IAAI,CAAC,QAAQ;IAC5B;IAEA;;GAEC,GACD,AAAQP,gBAAgBF,GAAa,EAA0B;QAC7D,OAAO;YACL,GAAG,IAAI,CAACxD,aAAa;YACrBmF,OAAOC,OAAO5B,IAAI2B,KAAK;YACvBzE,SAAS8C,IAAI6B,OAAO;QACtB;IACF;IAEA;;GAEC,GACD,AAAQT,YAAYjB,QAAgB,EAA0B;QAC5D,MAAMF,SAAiC,CAAC;QACxC,MAAM6B,QAAQ3B,SAASoB,KAAK,CAAC;QAE7B,KAAK,MAAMQ,QAAQD,MAAO;YACxB,MAAM,CAACE,KAAKC,MAAM,GAAGF,KAAKR,KAAK,CAAC;YAChCtB,MAAM,CAAC+B,IAAI,GAAGC,MAAMC,OAAO,CAAC,UAAU;QACxC;QAEA,OAAOjC;IACT;IAEA;;GAEC,GACDc,UAAUrD,KAAe,EAAU;QACjC,MAAMyE,YAAY;YAChBvB,WAAWlD,MAAMkD,SAAS;YAC1Be,OAAOjE,MAAMiE,KAAK;YAClBS,SAAS1E,MAAM0E,OAAO;YACtBP,SAASnE,MAAMmE,OAAO;YACtB,GAAInE,MAAM2E,aAAa,IAAI;gBAAEA,eAAe3E,MAAM2E,aAAa;YAAC,CAAC;YACjE,GAAI3E,MAAM4E,MAAM,IAAI;gBAAEA,QAAQ5E,MAAM4E,MAAM;YAAC,CAAC;YAC5C,GAAI5E,MAAM6E,OAAO,IAAI;gBAAEA,SAAS7E,MAAM6E,OAAO;YAAC,CAAC;YAC/C,GAAI7E,MAAM8E,OAAO,IAAI;gBAAEA,SAAS9E,MAAM8E,OAAO;YAAC,CAAC;YAC/C,GAAI9E,MAAM+E,QAAQ,IAAI;gBAAEA,UAAU/E,MAAM+E,QAAQ;YAAC,CAAC;YAClD,GAAI/E,MAAMY,KAAK,IAAI;gBAAEA,OAAOZ,MAAMY,KAAK;YAAC,CAAC;QAC3C;QAEA,OAAOW,KAAKC,SAAS,CAACiD;IACxB;IAEA;;GAEC,GACD,AAAQvE,eAAeF,KAAe,EAAQ;QAC5C,4BAA4B;QAC5B,IAAI,CAACA,MAAMkD,SAAS,EAAE;YACpBlD,MAAMkD,SAAS,GAAG,IAAIxC,OAAOC,WAAW;QAC1C;QAEA,4BAA4B;QAC5B,IAAI,CAAC,sCAAsCqE,IAAI,CAAChF,MAAMkD,SAAS,GAAG;YAChE,IAAI;gBACFlD,MAAMkD,SAAS,GAAG,IAAIxC,KAAKV,MAAMkD,SAAS,EAAEvC,WAAW;YACzD,EAAE,OAAM;gBACNX,MAAMkD,SAAS,GAAG,IAAIxC,OAAOC,WAAW;YAC1C;QACF;QAEA,yBAAyB;QACzBX,MAAMiE,KAAK,GAAGC,OAAOlE,MAAMiE,KAAK,EAAEgB,WAAW;IAC/C;IAEA;;GAEC,GACDC,aAAalF,KAAe,EAAEmF,aAAqB,EAAW;QAC5D,IAAI;YACF,MAAMC,UAAU,IAAI1E,KAAKV,MAAMkD,SAAS,EAAEC,OAAO;YACjD,MAAMkC,MAAM,IAAI3E,OAAOyC,OAAO;YAC9B,MAAMmC,cAAcH,gBAAgB,KAAK,KAAK,KAAK;YAEnD,OAAOE,MAAMD,UAAUE;QACzB,EAAE,OAAM;YACN,OAAO;QACT;IACF;IAEA;;GAEC,GACD,MAAMC,mBAAmBJ,aAAqB,EAAmB;QAC/D,IAAIK,eAAe;QAEnB,IAAI;YACF,oBAAoB;YACpB,MAAMC,cAAc,IAAI,CAACvG,MAAM,CAACkB,MAAM;YACtC,IAAI,CAAClB,MAAM,GAAG,IAAI,CAACA,MAAM,CAACwG,MAAM,CAACpD,CAAAA,MAAO,CAAC,IAAI,CAAC4C,YAAY,CAAC5C,KAAK6C;YAChEK,gBAAgBC,cAAc,IAAI,CAACvG,MAAM,CAACkB,MAAM;YAEhD,gCAAgC;YAChC,MAAMuF,YAAY,MAAM,IAAI,CAACC,iBAAiB;YAC9C,KAAK,MAAMtD,OAAOqD,UAAW;gBAC3B,IAAI,IAAI,CAACT,YAAY,CAAC5C,KAAK6C,gBAAgB;oBACzCK;gBACF;YACF;QACF,EAAE,OAAO5E,OAAO;YACdiF,QAAQjF,KAAK,CAAC,mCAAmCA;QACnD;QAEA,OAAO4E;IACT;IAEA;;GAEC,GACDM,gBAAgBxG,OAAqB,EAAU;QAC7C,IAAIyG,QAAQ;QAEZ,IAAIzG,QAAQ2E,KAAK,EAAE;YACjB8B,SAAS,CAAC,OAAO,EAAEzG,QAAQ2E,KAAK,CAAC,EAAE,CAAC;QACtC;QAEA,IAAI3E,QAAQE,OAAO,EAAE;YACnBuG,SAAS,CAAC,SAAS,EAAEzG,QAAQE,OAAO,CAAC,EAAE,CAAC;QAC1C;QAEAuG,QAAQA,MAAMvB,OAAO,CAAC,OAAO;QAE7B,IAAIlF,QAAQqF,aAAa,EAAE;YACzBoB,SAAS,CAAC,uBAAuB,EAAEzG,QAAQqF,aAAa,CAAC,CAAC,CAAC;QAC7D;QAEA,IAAIrF,QAAQ0G,SAAS,EAAE;YACrB,MAAMC,QAAQC,KAAKC,KAAK,CAAC7G,QAAQ0G,SAAS,CAACC,KAAK,CAAC9C,OAAO,KAAK;YAC7D,MAAMiD,MAAMF,KAAKC,KAAK,CAAC7G,QAAQ0G,SAAS,CAACI,GAAG,CAACjD,OAAO,KAAK;YACzD4C,QAAQ,GAAGE,MAAM,EAAE,EAAEG,IAAI,EAAE,EAAEL,OAAO;QACtC;QAEA,OAAOA;IACT;IAEA;;GAEC,GACDM,aAA6B;QAC3B,OAAO;YACL,GAAG,IAAI,CAACjH,OAAO;YACfM,cAAc,IAAI,CAACR,MAAM,CAACkB,MAAM;QAClC;IACF;IAEA;;GAEC,GACDkG,eAAuB;QACrB,IAAI,IAAI,CAAClH,OAAO,CAACK,SAAS,KAAK,GAAG;YAChC,OAAO;QACT;QAEA,MAAM8G,YAAY,IAAI,CAACrH,MAAM,CAACwG,MAAM,CAACpD,CAAAA,MAAOA,IAAI2B,KAAK,cAAqB7D,MAAM;QAChF,OAAOmG,YAAY,IAAI,CAACnH,OAAO,CAACK,SAAS;IAC3C;IAEA;;GAEC,GACD,MAAMmG,oBAAyC;QAC7C,MAAM9E,OAAmB,EAAE;QAE3B,IAAI;YACF,MAAM0F,QAAQ,MAAMlI,GAAGmI,OAAO,CAAC,IAAI,CAACxH,UAAU;YAE9C,KAAK,MAAMyH,QAAQF,MAAO;gBACxB,IAAI;oBACF,MAAMG,WAAWpI,KAAKwE,IAAI,CAAC,IAAI,CAAC9D,UAAU,EAAEyH;oBAC5C,MAAME,UAAU,MAAMtI,GAAGuI,QAAQ,CAACF,UAAU;oBAC5C,MAAMG,WAAWvF,KAAKwF,KAAK,CAACH;oBAC5B9F,KAAKX,IAAI,CAAC2G;gBACZ,EAAE,OAAM;gBACN,qBAAqB;gBACvB;YACF;QACF,EAAE,OAAM;QACN,gCAAgC;QAClC;QAEA,OAAOhG;IACT;IAEA;;GAEC,GACD,MAAcD,YAAYC,IAAgB,EAAiB;QACzD,IAAI;YACF,MAAMxC,GAAG0I,KAAK,CAAC,IAAI,CAAC/H,UAAU,EAAE;gBAAEgI,WAAW;YAAK;YAElD,KAAK,MAAM3E,OAAOxB,KAAM;gBACtB,MAAMoG,WAAW,GAAGxG,KAAK2E,GAAG,GAAG,CAAC,EAAEa,KAAKiB,MAAM,GAAGhF,QAAQ,CAAC,IAAIiF,KAAK,CAAC,GAAG,KAAK,CAAC;gBAC5E,MAAMT,WAAWpI,KAAKwE,IAAI,CAAC,IAAI,CAAC9D,UAAU,EAAEiI;gBAC5C,MAAM5I,GAAG+I,SAAS,CAACV,UAAUpF,KAAKC,SAAS,CAACc;YAC9C;QACF,EAAE,OAAO1B,OAAO;YACdiF,QAAQjF,KAAK,CAAC,0BAA0BA;QAC1C;IACF;IAEA;;GAEC,GACD,MAAcM,iBACZoG,EAAoB,EACpBC,UAAU,CAAC,EACC;QACZ,IAAI;YACF,OAAO,MAAMD;QACf,EAAE,OAAO1G,OAAO;YACd,IAAI2G,UAAU,IAAI,CAACxI,aAAa,EAAE;gBAChC,MAAMyI,QAAQ,IAAI,CAACxI,UAAU,GAAGkH,KAAKuB,GAAG,CAAC,GAAGF;gBAC5C,MAAM,IAAIG,QAAQC,CAAAA,UAAWC,WAAWD,SAASH;gBACjD,OAAO,IAAI,CAACtG,gBAAgB,CAACoG,IAAIC,UAAU;YAC7C;YACA,MAAM3G;QACR;IACF;IAEA;;GAEC,GACD,AAAQd,iBAAuB;QAC7B,IAAI,CAACX,UAAU,GAAG0I,YAAY;YAC5B,IAAI,IAAI,CAAC3I,MAAM,CAACkB,MAAM,GAAG,KAAK,CAAC,IAAI,CAACf,MAAM,EAAE;gBAC1C,IAAI;oBACF,MAAM,IAAI,CAACgB,KAAK;gBAClB,EAAE,OAAOO,OAAO;oBACdiF,QAAQjF,KAAK,CAAC,qBAAqBA;gBACrC;YACF;QACF,GAAG,IAAI,CAAC/B,aAAa;IACvB;IAEA;;GAEC,GACD,MAAMiJ,QAAuB;QAC3B,IAAI,CAACzI,MAAM,GAAG;QAEd,IAAI,IAAI,CAACF,UAAU,EAAE;YACnB4I,cAAc,IAAI,CAAC5I,UAAU;QAC/B;QAEA,uBAAuB;QACvB,IAAI,IAAI,CAACD,MAAM,CAACkB,MAAM,GAAG,GAAG;YAC1B,IAAI;gBACF,MAAM,IAAI,CAACC,KAAK;YAClB,EAAE,OAAOO,OAAO;gBACdiF,QAAQjF,KAAK,CAAC,kCAAkCA;YAClD;QACF;IACF;IAEA;;GAEC,GACDoH,aAAqB;QACnB,OAAO,IAAI,CAACtJ,OAAO;IACrB;IAEAuJ,mBAA2C;QACzC,OAAO,IAAI,CAACnJ,aAAa;IAC3B;IAEAoJ,gBAAwB;QACtB,OAAO,IAAI,CAACtJ,UAAU;IACxB;IAEAuJ,mBAA2B;QACzB,OAAO,IAAI,CAACtJ,aAAa;IAC3B;AACF"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Logging Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides structured JSON logging for consistent log output across the application.
|
|
5
|
+
* Part of Task 0.5: Implementation Tooling & Utilities (Foundation)
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const logger = createLogger('database-service');
|
|
9
|
+
* logger.info('Connection established', { host: 'localhost', port: 5432 });
|
|
10
|
+
*/ import * as fs from 'fs';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
/**
|
|
13
|
+
* Log levels in order of severity
|
|
14
|
+
*/ export var LogLevel = /*#__PURE__*/ function(LogLevel) {
|
|
15
|
+
LogLevel["DEBUG"] = "debug";
|
|
16
|
+
LogLevel["INFO"] = "info";
|
|
17
|
+
LogLevel["WARN"] = "warn";
|
|
18
|
+
LogLevel["ERROR"] = "error";
|
|
19
|
+
return LogLevel;
|
|
20
|
+
}({});
|
|
21
|
+
/**
|
|
22
|
+
* Log level priorities for filtering
|
|
23
|
+
*/ const LOG_LEVEL_PRIORITY = {
|
|
24
|
+
["debug"]: 0,
|
|
25
|
+
["info"]: 1,
|
|
26
|
+
["warn"]: 2,
|
|
27
|
+
["error"]: 3
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Create a structured logger instance
|
|
31
|
+
*
|
|
32
|
+
* @param context - Logger context (e.g., 'database-service', 'api-handler')
|
|
33
|
+
* @param options - Logger configuration options
|
|
34
|
+
* @returns Logger instance
|
|
35
|
+
*/ export function createLogger(context, options = {}) {
|
|
36
|
+
const { minLevel = "info", console: consoleOutput = true, filePath, pretty = false } = options;
|
|
37
|
+
let currentMinLevel = minLevel;
|
|
38
|
+
let fileStream = null;
|
|
39
|
+
// Initialize file stream if file path provided
|
|
40
|
+
if (filePath) {
|
|
41
|
+
const logDir = path.dirname(filePath);
|
|
42
|
+
if (!fs.existsSync(logDir)) {
|
|
43
|
+
fs.mkdirSync(logDir, {
|
|
44
|
+
recursive: true
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
fileStream = fs.createWriteStream(filePath, {
|
|
48
|
+
flags: 'a'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Write log entry to outputs
|
|
53
|
+
*/ function writeLog(entry) {
|
|
54
|
+
// Check if log level meets minimum threshold
|
|
55
|
+
if (LOG_LEVEL_PRIORITY[entry.level] < LOG_LEVEL_PRIORITY[currentMinLevel]) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const jsonString = pretty ? JSON.stringify(entry, null, 2) : JSON.stringify(entry);
|
|
59
|
+
// Console output
|
|
60
|
+
if (consoleOutput) {
|
|
61
|
+
switch(entry.level){
|
|
62
|
+
case "debug":
|
|
63
|
+
console.debug(jsonString);
|
|
64
|
+
break;
|
|
65
|
+
case "info":
|
|
66
|
+
console.info(jsonString);
|
|
67
|
+
break;
|
|
68
|
+
case "warn":
|
|
69
|
+
console.warn(jsonString);
|
|
70
|
+
break;
|
|
71
|
+
case "error":
|
|
72
|
+
console.error(jsonString);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// File output
|
|
77
|
+
if (fileStream) {
|
|
78
|
+
fileStream.write(jsonString + '\n');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Create log entry
|
|
83
|
+
*/ function createLogEntry(level, message, metadata, error) {
|
|
84
|
+
const entry = {
|
|
85
|
+
timestamp: new Date().toISOString(),
|
|
86
|
+
level,
|
|
87
|
+
context,
|
|
88
|
+
message
|
|
89
|
+
};
|
|
90
|
+
if (metadata && Object.keys(metadata).length > 0) {
|
|
91
|
+
entry.metadata = metadata;
|
|
92
|
+
}
|
|
93
|
+
if (error) {
|
|
94
|
+
entry.error = {
|
|
95
|
+
name: error.name,
|
|
96
|
+
message: error.message,
|
|
97
|
+
stack: error.stack
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return entry;
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
debug (message, metadata) {
|
|
104
|
+
const entry = createLogEntry("debug", message, metadata);
|
|
105
|
+
writeLog(entry);
|
|
106
|
+
},
|
|
107
|
+
info (message, metadata) {
|
|
108
|
+
const entry = createLogEntry("info", message, metadata);
|
|
109
|
+
writeLog(entry);
|
|
110
|
+
},
|
|
111
|
+
warn (message, metadata) {
|
|
112
|
+
const entry = createLogEntry("warn", message, metadata);
|
|
113
|
+
writeLog(entry);
|
|
114
|
+
},
|
|
115
|
+
error (message, error, metadata) {
|
|
116
|
+
const entry = createLogEntry("error", message, metadata, error);
|
|
117
|
+
writeLog(entry);
|
|
118
|
+
},
|
|
119
|
+
setMinLevel (level) {
|
|
120
|
+
currentMinLevel = level;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Global logger instance (default context)
|
|
126
|
+
*/ let globalLogger = null;
|
|
127
|
+
/**
|
|
128
|
+
* Get or create global logger
|
|
129
|
+
*
|
|
130
|
+
* @param options - Logger configuration options
|
|
131
|
+
* @returns Global logger instance
|
|
132
|
+
*/ export function getGlobalLogger(options) {
|
|
133
|
+
if (!globalLogger) {
|
|
134
|
+
globalLogger = createLogger('global', options);
|
|
135
|
+
}
|
|
136
|
+
return globalLogger;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Set global logger instance
|
|
140
|
+
*
|
|
141
|
+
* @param logger - Logger instance to use globally
|
|
142
|
+
*/ export function setGlobalLogger(logger) {
|
|
143
|
+
globalLogger = logger;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//# sourceMappingURL=logging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/logging.ts"],"sourcesContent":["/**\r\n * Structured Logging Utility\r\n *\r\n * Provides structured JSON logging for consistent log output across the application.\r\n * Part of Task 0.5: Implementation Tooling & Utilities (Foundation)\r\n *\r\n * Usage:\r\n * const logger = createLogger('database-service');\r\n * logger.info('Connection established', { host: 'localhost', port: 5432 });\r\n */\r\n\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport * as util from 'util';\r\n\r\n/**\r\n * Log levels in order of severity\r\n */\r\nexport enum LogLevel {\r\n DEBUG = 'debug',\r\n INFO = 'info',\r\n WARN = 'warn',\r\n ERROR = 'error',\r\n}\r\n\r\n/**\r\n * Structured log entry\r\n */\r\nexport interface LogEntry {\r\n timestamp: string;\r\n level: LogLevel;\r\n context: string;\r\n message: string;\r\n metadata?: Record<string, any>;\r\n error?: {\r\n name: string;\r\n message: string;\r\n stack?: string;\r\n };\r\n}\r\n\r\n/**\r\n * Logger configuration options\r\n */\r\nexport interface LoggerOptions {\r\n /** Minimum log level to output (default: INFO) */\r\n minLevel?: LogLevel;\r\n /** Enable console output (default: true) */\r\n console?: boolean;\r\n /** File path for log output (optional) */\r\n filePath?: string;\r\n /** Pretty print JSON (default: false) */\r\n pretty?: boolean;\r\n}\r\n\r\n/**\r\n * Logger interface\r\n */\r\nexport interface Logger {\r\n debug(message: string, metadata?: Record<string, any>): void;\r\n info(message: string, metadata?: Record<string, any>): void;\r\n warn(message: string, metadata?: Record<string, any>): void;\r\n error(message: string, error?: Error, metadata?: Record<string, any>): void;\r\n setMinLevel(level: LogLevel): void;\r\n}\r\n\r\n/**\r\n * Log level priorities for filtering\r\n */\r\nconst LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\r\n [LogLevel.DEBUG]: 0,\r\n [LogLevel.INFO]: 1,\r\n [LogLevel.WARN]: 2,\r\n [LogLevel.ERROR]: 3,\r\n};\r\n\r\n/**\r\n * Create a structured logger instance\r\n *\r\n * @param context - Logger context (e.g., 'database-service', 'api-handler')\r\n * @param options - Logger configuration options\r\n * @returns Logger instance\r\n */\r\nexport function createLogger(context: string, options: LoggerOptions = {}): Logger {\r\n const {\r\n minLevel = LogLevel.INFO,\r\n console: consoleOutput = true,\r\n filePath,\r\n pretty = false,\r\n } = options;\r\n\r\n let currentMinLevel = minLevel;\r\n let fileStream: fs.WriteStream | null = null;\r\n\r\n // Initialize file stream if file path provided\r\n if (filePath) {\r\n const logDir = path.dirname(filePath);\r\n if (!fs.existsSync(logDir)) {\r\n fs.mkdirSync(logDir, { recursive: true });\r\n }\r\n fileStream = fs.createWriteStream(filePath, { flags: 'a' });\r\n }\r\n\r\n /**\r\n * Write log entry to outputs\r\n */\r\n function writeLog(entry: LogEntry): void {\r\n // Check if log level meets minimum threshold\r\n if (LOG_LEVEL_PRIORITY[entry.level] < LOG_LEVEL_PRIORITY[currentMinLevel]) {\r\n return;\r\n }\r\n\r\n const jsonString = pretty\r\n ? JSON.stringify(entry, null, 2)\r\n : JSON.stringify(entry);\r\n\r\n // Console output\r\n if (consoleOutput) {\r\n switch (entry.level) {\r\n case LogLevel.DEBUG:\r\n console.debug(jsonString);\r\n break;\r\n case LogLevel.INFO:\r\n console.info(jsonString);\r\n break;\r\n case LogLevel.WARN:\r\n console.warn(jsonString);\r\n break;\r\n case LogLevel.ERROR:\r\n console.error(jsonString);\r\n break;\r\n }\r\n }\r\n\r\n // File output\r\n if (fileStream) {\r\n fileStream.write(jsonString + '\\n');\r\n }\r\n }\r\n\r\n /**\r\n * Create log entry\r\n */\r\n function createLogEntry(\r\n level: LogLevel,\r\n message: string,\r\n metadata?: Record<string, any>,\r\n error?: Error\r\n ): LogEntry {\r\n const entry: LogEntry = {\r\n timestamp: new Date().toISOString(),\r\n level,\r\n context,\r\n message,\r\n };\r\n\r\n if (metadata && Object.keys(metadata).length > 0) {\r\n entry.metadata = metadata;\r\n }\r\n\r\n if (error) {\r\n entry.error = {\r\n name: error.name,\r\n message: error.message,\r\n stack: error.stack,\r\n };\r\n }\r\n\r\n return entry;\r\n }\r\n\r\n return {\r\n debug(message: string, metadata?: Record<string, any>): void {\r\n const entry = createLogEntry(LogLevel.DEBUG, message, metadata);\r\n writeLog(entry);\r\n },\r\n\r\n info(message: string, metadata?: Record<string, any>): void {\r\n const entry = createLogEntry(LogLevel.INFO, message, metadata);\r\n writeLog(entry);\r\n },\r\n\r\n warn(message: string, metadata?: Record<string, any>): void {\r\n const entry = createLogEntry(LogLevel.WARN, message, metadata);\r\n writeLog(entry);\r\n },\r\n\r\n error(message: string, error?: Error, metadata?: Record<string, any>): void {\r\n const entry = createLogEntry(LogLevel.ERROR, message, metadata, error);\r\n writeLog(entry);\r\n },\r\n\r\n setMinLevel(level: LogLevel): void {\r\n currentMinLevel = level;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Global logger instance (default context)\r\n */\r\nlet globalLogger: Logger | null = null;\r\n\r\n/**\r\n * Get or create global logger\r\n *\r\n * @param options - Logger configuration options\r\n * @returns Global logger instance\r\n */\r\nexport function getGlobalLogger(options?: LoggerOptions): Logger {\r\n if (!globalLogger) {\r\n globalLogger = createLogger('global', options);\r\n }\r\n return globalLogger;\r\n}\r\n\r\n/**\r\n * Set global logger instance\r\n *\r\n * @param logger - Logger instance to use globally\r\n */\r\nexport function setGlobalLogger(logger: Logger): void {\r\n globalLogger = logger;\r\n}\r\n"],"names":["fs","path","LogLevel","LOG_LEVEL_PRIORITY","createLogger","context","options","minLevel","console","consoleOutput","filePath","pretty","currentMinLevel","fileStream","logDir","dirname","existsSync","mkdirSync","recursive","createWriteStream","flags","writeLog","entry","level","jsonString","JSON","stringify","debug","info","warn","error","write","createLogEntry","message","metadata","timestamp","Date","toISOString","Object","keys","length","name","stack","setMinLevel","globalLogger","getGlobalLogger","setGlobalLogger","logger"],"mappings":"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"}
|