#feature: add AI log analysis & some bugfix
This commit is contained in:
315
app/Http/Controllers/LogAnalysisController.php
Normal file
315
app/Http/Controllers/LogAnalysisController.php
Normal file
@@ -0,0 +1,315 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\AnalysisMode;
|
||||
use App\Services\AiService;
|
||||
use App\Services\ConfigService;
|
||||
use App\Services\LogAnalysisService;
|
||||
use App\Services\SlsService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LogAnalysisController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly LogAnalysisService $analysisService,
|
||||
private readonly SlsService $slsService,
|
||||
private readonly AiService $aiService,
|
||||
private readonly ConfigService $configService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 查询日志(预览,不进行 AI 分析)
|
||||
*/
|
||||
public function queryLogs(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'from' => 'required|date',
|
||||
'to' => 'required|date|after:from',
|
||||
'query' => 'nullable|string|max:1000',
|
||||
'limit' => 'nullable|integer|min:1|max:1000',
|
||||
'logstores' => 'nullable|array',
|
||||
'logstores.*' => 'string',
|
||||
]);
|
||||
|
||||
if (!$this->slsService->isConfigured()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'SLS 服务未配置,请检查 .env 中的 SLS_* 配置项',
|
||||
], 400);
|
||||
}
|
||||
|
||||
try {
|
||||
// 如果指定了多个 logstore,使用多 logstore 查询
|
||||
if (!empty($validated['logstores']) && count($validated['logstores']) > 1) {
|
||||
$result = $this->analysisService->queryLogsFromMultipleStores(
|
||||
Carbon::parse($validated['from']),
|
||||
Carbon::parse($validated['to']),
|
||||
$validated['query'] ?? null,
|
||||
$validated['limit'] ?? 100,
|
||||
$validated['logstores']
|
||||
);
|
||||
} else {
|
||||
// 单个 logstore 查询
|
||||
$result = $this->analysisService->queryLogs(
|
||||
Carbon::parse($validated['from']),
|
||||
Carbon::parse($validated['to']),
|
||||
$validated['query'] ?? null,
|
||||
$validated['limit'] ?? 100
|
||||
);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $result,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
|
||||
// 优化常见错误提示
|
||||
if (str_contains($errorMessage, 'key') && str_contains($errorMessage, 'is not config as key value config')) {
|
||||
$errorMessage = 'SLS 查询语法错误:使用 "字段:值" 语法需要该字段配置为键值索引。建议使用全文搜索(如 "ERROR")或 SQL 语法(如 "* | where level = \"ERROR\"")';
|
||||
} elseif (str_contains($errorMessage, 'ParameterInvalid')) {
|
||||
$errorMessage = 'SLS 查询参数无效:' . $errorMessage . '。请检查查询语法是否正确';
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => $errorMessage,
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的 logstore 列表
|
||||
*/
|
||||
public function getLogstores(): JsonResponse
|
||||
{
|
||||
if (!$this->slsService->isConfigured()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'SLS 服务未配置',
|
||||
], 400);
|
||||
}
|
||||
|
||||
try {
|
||||
$logstores = $this->slsService->getLogstores();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'logstores' => $logstores,
|
||||
],
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行异步分析(创建后台任务)
|
||||
*/
|
||||
public function analyze(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'from' => 'required|date',
|
||||
'to' => 'required|date|after:from',
|
||||
'query' => 'nullable|string|max:1000',
|
||||
'mode' => 'nullable|in:logs,logs+code',
|
||||
]);
|
||||
|
||||
if (!$this->slsService->isConfigured()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'SLS 服务未配置',
|
||||
], 400);
|
||||
}
|
||||
|
||||
if (!$this->aiService->isConfigured()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'AI 服务未配置,请在设置页面配置 AI 提供商',
|
||||
], 400);
|
||||
}
|
||||
|
||||
$mode = ($validated['mode'] ?? 'logs') === 'logs+code'
|
||||
? AnalysisMode::LogsWithCode
|
||||
: AnalysisMode::Logs;
|
||||
|
||||
try {
|
||||
// 创建异步分析任务
|
||||
$report = $this->analysisService->analyzeAsync(
|
||||
Carbon::parse($validated['from']),
|
||||
Carbon::parse($validated['to']),
|
||||
$validated['query'] ?? null,
|
||||
$mode
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '分析任务已创建,请在历史报告中查看结果',
|
||||
'data' => [
|
||||
'report_id' => $report->id,
|
||||
'status' => $report->status,
|
||||
],
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取历史报告列表
|
||||
*/
|
||||
public function listReports(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'limit' => 'nullable|integer|min:1|max:100',
|
||||
'offset' => 'nullable|integer|min:0',
|
||||
]);
|
||||
|
||||
$result = $this->analysisService->getReports(
|
||||
$validated['limit'] ?? 20,
|
||||
$validated['offset'] ?? 0
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $result,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个报告详情
|
||||
*/
|
||||
public function getReport(int $id): JsonResponse
|
||||
{
|
||||
$report = $this->analysisService->getReport($id);
|
||||
|
||||
if (!$report) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '报告不存在',
|
||||
], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $report,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*/
|
||||
public function getConfig(): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'sls_configured' => $this->slsService->isConfigured(),
|
||||
'ai_configured' => $this->aiService->isConfigured(),
|
||||
'ai_providers' => $this->aiService->getProviders(),
|
||||
'active_ai_provider' => $this->aiService->getActiveProvider(),
|
||||
'app_env_map' => $this->configService->get('log_analysis.app_env_map', []),
|
||||
'settings' => $this->configService->get('log_analysis.settings', [
|
||||
'max_logs_per_batch' => 1000,
|
||||
'max_logs_per_app' => 500,
|
||||
'default_time_window_minutes' => 60,
|
||||
'schedule_enabled' => false,
|
||||
]),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新配置(需要管理员权限)
|
||||
*/
|
||||
public function updateConfig(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'app_env_map' => 'nullable|array',
|
||||
'settings' => 'nullable|array',
|
||||
'ai_providers' => 'nullable|array',
|
||||
'active_ai_provider' => 'nullable|string',
|
||||
]);
|
||||
|
||||
try {
|
||||
if (isset($validated['app_env_map'])) {
|
||||
$this->configService->set(
|
||||
'log_analysis.app_env_map',
|
||||
$validated['app_env_map'],
|
||||
'Log analysis app to project/env mapping'
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($validated['settings'])) {
|
||||
$this->configService->set(
|
||||
'log_analysis.settings',
|
||||
$validated['settings'],
|
||||
'Log analysis settings'
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($validated['ai_providers'])) {
|
||||
$this->aiService->saveProviders($validated['ai_providers']);
|
||||
}
|
||||
|
||||
if (isset($validated['active_ai_provider'])) {
|
||||
$this->aiService->setActiveProvider($validated['active_ai_provider']);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '配置已更新',
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 SLS 连接
|
||||
*/
|
||||
public function testSlsConnection(): JsonResponse
|
||||
{
|
||||
if (!$this->slsService->isConfigured()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'SLS 服务未配置',
|
||||
]);
|
||||
}
|
||||
|
||||
$connected = $this->slsService->testConnection();
|
||||
|
||||
return response()->json([
|
||||
'success' => $connected,
|
||||
'message' => $connected ? 'SLS 连接成功' : 'SLS 连接失败',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 AI 连接
|
||||
*/
|
||||
public function testAiConnection(): JsonResponse
|
||||
{
|
||||
$result = $this->aiService->testConnection();
|
||||
|
||||
return response()->json([
|
||||
'success' => $result['success'],
|
||||
'message' => $result['message'],
|
||||
'data' => $result,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user