feat(logging): Implement database logging with app_log table
- Created migration to set up app_log table for storing application logs. - Added AppLog entity and repository for ORM handling of logs. - Developed DbLogger service to persist logs of level WARNING and above to the database while maintaining existing logging behavior. - Implemented tests for admin log retrieval and DbLogger functionality to ensure proper logging behavior. - Enhanced logging context sanitization for better error tracking.
This commit is contained in:
@@ -18,6 +18,7 @@ use App\Representation\Entity\Representation;
|
||||
use App\Secretary\Entity\DoctorSecretary;
|
||||
use App\Settlement\Entity\FinancialBreakdown;
|
||||
use App\Settlement\Entity\Settlement;
|
||||
use App\Shared\Logging\AppLog;
|
||||
use App\Sms\Entity\SmsLog;
|
||||
use App\Sms\Entity\SmsTemplate;
|
||||
use App\Shared\Controller\BaseController;
|
||||
@@ -1929,4 +1930,63 @@ class AdminApiController extends BaseController
|
||||
'period' => ['from' => $from, 'to' => $to],
|
||||
]);
|
||||
}
|
||||
|
||||
// ── Application logs ────────────────────────────────────────────────────────
|
||||
|
||||
#[OA\Get(
|
||||
path: '/api/v1/admin/logs',
|
||||
summary: 'List persisted application logs (paginated)',
|
||||
security: [['bearerAuth' => []]],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
|
||||
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 25)),
|
||||
new OA\Parameter(name: 'level', in: 'query', required: false, description: 'PSR level filter (warning/error/critical/...)', schema: new OA\Schema(type: 'string')),
|
||||
new OA\Parameter(name: 'search', in: 'query', required: false, description: 'substring match on message', schema: new OA\Schema(type: 'string')),
|
||||
new OA\Parameter(name: 'from', in: 'query', required: false, description: 'unix timestamp lower bound', schema: new OA\Schema(type: 'integer')),
|
||||
new OA\Parameter(name: 'to', in: 'query', required: false, description: 'unix timestamp upper bound', schema: new OA\Schema(type: 'integer')),
|
||||
],
|
||||
responses: [new OA\Response(response: 200, description: 'Paginated list of logs')]
|
||||
)]
|
||||
#[Route('/api/v1/admin/logs', methods: ['GET'])]
|
||||
public function logs(Request $request): JsonResponse
|
||||
{
|
||||
$page = max(1, (int) $request->query->get('page', 1));
|
||||
$limit = min(100, max(5, (int) $request->query->get('limit', 25)));
|
||||
$level = trim((string) $request->query->get('level', ''));
|
||||
$search = trim((string) $request->query->get('search', ''));
|
||||
$from = trim((string) $request->query->get('from', ''));
|
||||
$to = trim((string) $request->query->get('to', ''));
|
||||
|
||||
$qb = $this->em->createQueryBuilder()
|
||||
->select('l.id, l.level, l.message, l.context, l.channel, l.path, l.createdAt')
|
||||
->from(AppLog::class, 'l');
|
||||
|
||||
if ($level !== '') {
|
||||
$qb->andWhere('l.level = :level')->setParameter('level', $level);
|
||||
}
|
||||
if ($search !== '') {
|
||||
$qb->andWhere('l.message LIKE :s')->setParameter('s', '%' . $search . '%');
|
||||
}
|
||||
if ($from !== '') {
|
||||
$qb->andWhere('l.createdAt >= :from')->setParameter('from', (int) $from);
|
||||
}
|
||||
if ($to !== '') {
|
||||
$qb->andWhere('l.createdAt <= :to')->setParameter('to', (int) $to);
|
||||
}
|
||||
|
||||
$qb->orderBy('l.id', 'DESC');
|
||||
|
||||
$total = (clone $qb)->select('COUNT(l.id)')->getQuery()->getSingleScalarResult();
|
||||
$rows = $qb->setFirstResult(($page - 1) * $limit)->setMaxResults($limit)->getQuery()->getArrayResult();
|
||||
|
||||
return $this->paginated(array_map(fn(array $l) => [
|
||||
'id' => (int) $l['id'],
|
||||
'level' => $l['level'],
|
||||
'message' => $l['message'],
|
||||
'context' => $l['context'],
|
||||
'channel' => $l['channel'],
|
||||
'path' => $l['path'],
|
||||
'created_at' => (int) $l['createdAt'],
|
||||
], $rows), (int) $total, $page, $limit);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user