SLS_0">Laravel对接SLS日志服务(写入和读取)
1、下载阿里云的sdk
php">#通过composer下载
composer require alibabacloud/aliyun-log-php-sdk#对应的git仓库
https://github.com/aliyun/aliyun-log-php-sdk
2、创建sdk请求的service
php"><?phpnamespace App\Services;use Aliyun_Log_Client;
use Aliyun_Log_Models_LogItem;
use Aliyun_Log_Models_PutLogsRequest;
use Aliyun_Log_Models_GetLogsRequest;
use Aliyun_Log_Models_GetHistogramsRequest;
use Exception;
use Illuminate\Support\Facades\Log;class SLSTimeSeriesService
{protected $client;protected $project;protected $logstore;public function __construct(){// 从配置中获取凭证$endpoint = env('ALIYUN_SLS_ENDPOINT');$accessKeyId = env('ALIYUN_ACCESS_KEY_ID');$accessKeySecret = env('ALIYUN_ACCESS_KEY_SECRET');// 验证配置if (!$endpoint || !$accessKeyId || !$accessKeySecret) {throw new Exception('SLS configuration is missing');}// 初始化客户端$this->client = new Aliyun_Log_Client($endpoint, $accessKeyId, $accessKeySecret);$this->project = env('ALIYUN_SLS_PROJECT');$this->logstore = env('ALIYUN_SLS_LOGSTORE');}/*** 查询日志(带分页和排序)*/public function getLogs($query = '', $from = null, $to = null, $page = 1, $perPage = 10, $sort = 'desc', $sortField = '__time__'){try {$from = $from ?: time() - 3600;$to = $to ?: time();// 计算偏移量$offset = ($page - 1) * $perPage;// 获取总数$histogramRequest = new Aliyun_Log_Models_GetHistogramsRequest($this->project,$this->logstore,$from,$to,'',$query ?: '*');$histogramResponse = $this->client->getHistograms($histogramRequest);$total = $histogramResponse->getTotalCount();// 创建日志查询请求$request = new Aliyun_Log_Models_GetLogsRequest($this->project,$this->logstore,$from,$to,'',$query ?: '*',1000, // 先获取较多数据以便排序0,true);// 执行查询$response = $this->client->getLogs($request);$logs = $response->getLogs();// 处理日志数据$formattedLogs = [];foreach ($logs as $log) {$contents = $log->getContents();$contents['log_time'] = date('Y-m-d H:i:s', $log->getTime());$formattedLogs[] = $contents;}// 自定义排序if ($sortField !== '__time__') {usort($formattedLogs, function($a, $b) use ($sortField, $sort) {// 确保字段存在$valueA = isset($a[$sortField]) ? $a[$sortField] : '';$valueB = isset($b[$sortField]) ? $b[$sortField] : '';// 如果是数字字符串,转换为数字比较if (is_numeric($valueA) && is_numeric($valueB)) {$valueA = (float)$valueA;$valueB = (float)$valueB;}// 根据排序方向比较if ($sort === 'asc') {return $valueA <=> $valueB;}return $valueB <=> $valueA;});}// 应用分页$formattedLogs = array_slice($formattedLogs, $offset, $perPage);// 返回结果return ['logs' => $formattedLogs,'pagination' => ['total' => $total,'per_page' => $perPage,'current_page' => $page,'last_page' => ceil($total / $perPage),'from' => $offset + 1,'to' => $offset + count($formattedLogs)]];} catch (Exception $e) {Log::error('SLS Get Error: ' . $e->getMessage());throw $e;}}/*** 写入日志*/public function putLogs($data){try {// 确保所有值都是字符串$contents = [];foreach ($data as $key => $value) {$contents[$key] = is_array($value) ? json_encode($value) : (string)$value;}// 创建日志内容$logItem = new Aliyun_Log_Models_LogItem();$logItem->setTime(time());$logItem->setContents($contents);// 创建请求$request = new Aliyun_Log_Models_PutLogsRequest($this->project,$this->logstore,'test_topic','',[$logItem]);// 发送日志$response = $this->client->putLogs($request);return true;} catch (Exception $e) {Log::error('SLS Put Error: ' . $e->getMessage());throw $e;}}/*** 查询所有日志(不分页)* @param string $query 查询条件* @param int|null $from 开始时间* @param int|null $to 结束时间* @return array* 循环,速度慢,不推荐使用*/public function getAllLogs($query = '', $from = null, $to = null, $sort = 'desc', $sortField = '__time__'){try {$from = $from ?: time() - 3600;$to = $to ?: time();// 构建查询语句$searchQuery = !empty($query) ? $query : '*';$allLogs = [];$offset = 0;$limit = 100; // 每次获取100条do {// 创建日志查询请求$request = new Aliyun_Log_Models_GetLogsRequest($this->project,$this->logstore,$from,$to,'', // topic$searchQuery, // 查询语句$limit, // 每次获取数量$offset, // 当前偏移量$sort === 'desc' // 是否倒序);// 执行查询$response = $this->client->getLogs($request);$logs = $response->getLogs();$count = count($logs);// 处理本批次的日志数据foreach ($logs as $log) {$contents = $log->getContents();// 解析 value 字段if (isset($contents['__value__']) && is_string($contents['__value__'])) {try {$decodedValue = json_decode($contents['__value__'], true);if (json_last_error() === JSON_ERROR_NONE) {$contents['__value__'] = $decodedValue;}} catch (\Exception $e) {// 保持原值}}$contents['log_time'] = date('Y-m-d H:i:s', $log->getTime());$allLogs[] = $contents;}// 更新偏移量$offset += $count;// 如果返回的数据少于限制数,说明已经没有更多数据if ($count < $limit) {break;}} while (true);// 如果需要按其他字段排序if ($sortField !== '__time__') {usort($allLogs, function($a, $b) use ($sortField, $sort) {$valueA = isset($a[$sortField]) ? $a[$sortField] : '';$valueB = isset($b[$sortField]) ? $b[$sortField] : '';if (is_numeric($valueA) && is_numeric($valueB)) {$valueA = (float)$valueA;$valueB = (float)$valueB;}return $sort === 'asc' ?($valueA <=> $valueB) :($valueB <=> $valueA);});}return ['logs' => $allLogs,'total' => count($allLogs),'query_info' => ['query' => $searchQuery,'from' => date('Y-m-d H:i:s', $from),'to' => date('Y-m-d H:i:s', $to),'sort_field' => $sortField,'sort_order' => $sort,'total_batches' => ceil($offset / $limit)]];} catch (Exception $e) {
// Log::error('SLS Get All Logs Error', [
// 'error' => $e->getMessage(),
// 'query' => $searchQuery ?? '',
// 'from' => date('Y-m-d H:i:s', $from),
// 'to' => date('Y-m-d H:i:s', $to),
// 'offset' => $offset ?? 0
// ]);throw $e;}}/*** 查询所有日志(不分页)* @param $query* @param $from* @param $to* @param $userId* @param $sortOrder* @return array* sql limit的方式,有排序限制,速度快*/public function getAllLogsSql($query = '', $from = null, $to = null, $userId = null, $sortOrder = 'ASC'){try {$from = $from ?: time() - 3600; // 默认查询最近1小时$to = $to ?: time();// 获取总数,会消耗一定的时间,数量越大,消耗的时间越多,如果想节约时间,可以设置一个最大值类型10W之类的
// $histogramRequest = new Aliyun_Log_Models_GetHistogramsRequest(
// $this->project,
// $this->logstore,
// $from,
// $to,
// '',
// $query ?: '*'
// );
//
// $histogramResponse = $this->client->getHistograms($histogramRequest);
// $total = $histogramResponse->getTotalCount();
// $maxResults = $total; // 一次性拉取的最大数量$maxResults = 100000; // 一次性拉取的最大数量// 构建基础查询语句$searchQuery = !empty($query) ? $query : '*';// 如果有 user_id 条件,添加筛选if ($userId) {
// $searchQuery .= sprintf(" AND user_id='%s'", $userId);$searchQuery .= sprintf(" AND user_id=%d", (int)$userId);}// Log::info('Starting Query', [
// 'query' => $searchQuery,
// 'from' => date('Y-m-d H:i:s', $from),
// 'to' => date('Y-m-d H:i:s', $to),
// 'sortOrder' => $sortOrder,
// ]);// SQL 查询语句,按时间排序$sqlQuery = sprintf("%s | SELECT * ORDER BY __time__ %s LIMIT %d",$searchQuery,strtoupper($sortOrder), // ASC 或 DESC$maxResults);// Log::info('Executing SQL Query', [
// 'query' => $sqlQuery,
// 'from' => date('Y-m-d H:i:s', $from),
// 'to' => date('Y-m-d H:i:s', $to),
// ]);// 发送请求$request = new Aliyun_Log_Models_GetLogsRequest($this->project,$this->logstore,$from,$to,'', // topic$sqlQuery, // 查询语句0, // batchSize 无用0, // offset 无用false // 不自动排序(已通过 SQL 排序));$response = $this->client->getLogs($request);$logs = $response->getLogs();$allLogs = [];// 处理返回的日志数据foreach ($logs as $log) {$contents = $log->getContents();// 解析 value 字段if (isset($contents['__value__']) && is_string($contents['__value__'])) {try {$decodedValue = json_decode($contents['__value__'], true);if (json_last_error() === JSON_ERROR_NONE) {$contents['__value__'] = $decodedValue;}} catch (\Exception $e) {// 保持原值}}$contents['log_time'] = date('Y-m-d H:i:s', $log->getTime());$allLogs[] = $contents;}// Log::info('Query Completed', [
// 'fetched' => count($allLogs),
// 'total_expected' => $maxResults,
// ]);return ['logs' => $allLogs,'total' => count($allLogs),'query_info' => ['query' => $searchQuery,'from' => date('Y-m-d H:i:s', $from),'to' => date('Y-m-d H:i:s', $to),'total_fetched' => count($allLogs),'sort_order' => $sortOrder,]];} catch (Exception $e) {
// Log::error('SLS Query Error', [
// 'error' => $e->getMessage(),
// 'query' => $searchQuery ?? '',
// 'from' => date('Y-m-d H:i:s', $from),
// 'to' => date('Y-m-d H:i:s', $to),
// 'sort_order' => $sortOrder,
// ]);throw $e;}}
}
3、创建config配置
php"># config/sls.config
<?php
return ['aliyun' => ['access_key_id' => env('ALIYUN_ACCESS_KEY_ID'),'access_key_secret' => env('ALIYUN_ACCESS_KEY_SECRET'),'sls' => ['endpoint' => env('ALIYUN_SLS_ENDPOINT'),'project' => env('ALIYUN_SLS_PROJECT'),'logstore' => env('ALIYUN_SLS_LOGSTORE'),],],
];
4、从阿里云获取对应的配置填写在env
php"># .env,同阿里云账号下的服务器是允许内网访问的
ALIYUN_SLS_ENDPOINT=cn-shenzhen.log.aliyuncs.com
#内网
#ALIYUN_SLS_ENDPOINT=cn-shenzhen-intranet.log.aliyuncs.com
ALIYUN_SLS_PROJECT=
ALIYUN_ACCESS_KEY_ID=
ALIYUN_ACCESS_KEY_SECRET=
ALIYUN_SLS_LOGSTORE=
5、创建控制器进行验证
php"><?phpnamespace App\Http\Controllers\Api;use App\Http\Controllers\Controller;
use App\Jobs\Sugar;
use App\Services\SLSTimeSeriesService;
use Exception;
use Illuminate\Http\Request;class MetricsController extends Controller
{protected $slsService;// 定义允许排序的字段// 定义允许排序的字段和默认排序方式protected $allowedSortFields = ['__time__', // 日志时间'__tag__:__receive_time__', // 接收时间'timestamp', // 自定义时间戳'user_id', // 自定义时间戳];public function __construct(SLSTimeSeriesService $slsService){$this->slsService = $slsService;}/*** 写入日志*/public function store(Request $request){try {$data = ['event' => 'user_login','user_id' => (string)$request->input('user_id', '1'),'ip' => $request->ip(),'timestamp' => (string)time(),'request_data' => $request->all()];$this->slsService->putLogs($data);return response()->json(['success' => true,'message' => 'Log stored successfully']);} catch (Exception $e) {return response()->json(['success' => false,'message' => $e->getMessage()], 500);}}//分页查询public function index(Request $request){try {// 构建查询条件$conditions = [];if ($request->has('user_id')) {$conditions[] = 'user_id = "' . $request->user_id . '"';}if ($request->has('event')) {$conditions[] = 'event = "' . $request->event . '"';}// 获取排序参数$sortField = $request->input('sort_by', '__time__'); // 默认使用日志时间排序$sortOrder = $request->input('sort', 'desc');// 验证排序字段if (!in_array($sortField, $this->allowedSortFields)) {$sortField = '__time__';}// 验证排序方向$sortOrder = strtolower($sortOrder) === 'asc' ? 'asc' : 'desc';// 构建基本查询$query = !empty($conditions) ? implode(' and ', $conditions) : '*';// 获取分页参数$page = (int)$request->input('page', 1);$perPage = (int)$request->input('per_page', 10);// 时间范围$from = $request->input('from') ? strtotime($request->input('from')) : time() - 3600;$to = $request->input('to') ? strtotime($request->input('to')) : time();// 记录查询参数// 获取数据$result = $this->slsService->getLogs($query,$from,$to,$page,$perPage,$sortOrder,$sortField);return response()->json(['success' => true,'data' => $result['logs'],'pagination' => $result['pagination'],'query_info' => ['query' => $query,'conditions' => $conditions,'sort' => ['field' => $sortField,'order' => $sortOrder],'from' => date('Y-m-d H:i:s', $from),'to' => date('Y-m-d H:i:s', $to)]]);} catch (Exception $e) {return response()->json(['success' => false,'message' => $e->getMessage(),'query_info' => ['query' => $query ?? '*','conditions' => $conditions ?? []]], 500);}}/*** @param Request $request* @return array* @throws Exception* 循环查询,速度慢,排序无限制,不分页*/public function all(Request $request){// 时间范围$from = $request->input('from') ? strtotime($request->input('from')) : time() - 3600;$to = $request->input('to') ? strtotime($request->input('to')) : time();// 自定义排序$result = $this->slsService->getAllLogs('*',$from,$to,'desc','timestamp');return $result;}/*** @param Request $request* @return array* @throws Exception* sql查询,速度快,排序有限制,需要有索引才能查询出来,不分页*/public function allSql(Request $request){$user_id = $request->input('user_id') ?? '';// 时间范围$from = $request->input('from') ? strtotime($request->input('from')) : time() - 3600;$to = $request->input('to') ? strtotime($request->input('to')) : time();$result = $this->slsService->getAllLogsSql('*',$from,$to,$user_id,'desc');return $result;}
}
6、效果如下