316 lines
9.8 KiB
PHP
316 lines
9.8 KiB
PHP
<?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,
|
||
]);
|
||
}
|
||
}
|