일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 리디렉트
- js 내부함수
- 아두이노 ESP8266
- 라즈베리파이
- 구글 클라우드 플랫폼
- js 반복문
- 아두이노 https
- CentOS8
- MariaDB
- Centos Node js
- 리다이렉트
- 아두이노
- 아두이노 DB
- 아두이노 https post
- 아두이노 fingerprint
- 라즈베리파이 3b+
- redirect
- js 내부함수 반복문
- Raspbian
- Apache
- js for 반복문
- 리디렉션
- Today
- Total
dinist
그누보드 sql_query 실행시 monolog 라이브러리를 활용한 에러로깅 본문
어떠한 글을 작성하면 DB에 저장이 되고, 메일로 알림을 발송하도록 되어있는 그누보드 기반 웹 사이트가 있다.
어느날 DB에는 저장이 되어 있지 않지만, 메일은 발송되었다는 연락이 있었다.
바로 테스트에 들어갔지만 DB에도 잘 저장되고 메일도 잘 전송되었다.
심지어 해당 문제 건만 DB에 저장되지 않았고 다른 사용자들의 게시글 저장과 메일 발송은 정상적이였다.
DB에 저장되지 않은 게시글의 발송 메일을 보니 sql 쿼리에 영향을 줄 내용은 찾지 못해 무엇이 원인인지는 파악하지 못하였으나 앞으로 이러한 일이 또 발생할 수 있으므로 쿼리에 오류가 발생한다면 로그를 남겨야할 필요성을 느꼈다.
.......
중략
.......
if(function_exists('mysqli_query') && G5_MYSQLI_USE) {
if ($error) {
$result = @mysqli_query($link, $sql) or die("<p>$sql<p>" . mysqli_errno($link) . " : " . mysqli_error($link) . "<p>error file : {$_SERVER['SCRIPT_NAME']}");
} else {
try {
$result = @mysqli_query($link, $sql);
} catch (Exception $e) {
$result = null;
}
}
} else {
if ($error) {
$result = @mysql_query($sql, $link) or die("<p>$sql<p>" . mysql_errno() . " : " . mysql_error() . "<p>error file : {$_SERVER['SCRIPT_NAME']}");
} else {
$result = @mysql_query($sql, $link);
}
}
.......
중략
.......
run_event('sql_query_after', $result, $sql, $start_time, $end_time, $error, $source);
위 코드는 그누보드의 common.lib.php 파일의 sql_query 함수의 일부이다.
쿼리 실행이 실패하면 false, 문제가 생긴다면 null을 리턴할 것이다.
코드 마지막에 sql_query_after라는 태그의 hook을 사용할 수 있도록 되어있다.
나는 /bbs 폴더에서 실행하는 모든 코드에 대한 sql_query 에러 로그를 남기고 싶었다.
그래서 /bbs/_common.php 파일에 hook을 추가하기로 결정
원래는 fopen fwrite로 아래 처럼 직접 로그를 남겨보려 했지만..!
<?php
....
중략
....
require_once G5_LIB_PATH."/vendor/autoload.php";
add_event('sql_query_after','sql_error_logger',NULL,6);
function sql_error_logger(...$params){
if($params[0] === false || is_null($params[0])){
$log_path = G5_DATA_PATH."/log/sql_error/";
if(!is_dir($log_path))
mkdir($log_path,G5_DIR_PERMISSION, true);
$sql_error_resource = fopen($log_path.G5_TIME_YMD.".log","a");
if($sql_error_resource){
$result = array_slice($params,1);
$time = G5_TIME_YMDHIS;
$eol = PHP_EOL;
$log = <<<LOG
[$time] raw_sql : {$result[0]}
startTimeStamp : {$result[1]} , endTimeStamp : {$result[2]}
errorCode : {$result[3]['error_code']}, errorMessage : {$result[3]['error_message']}
fileAt : {$result[4]['file']} , fileLine : {$result[4]['line']}{$eol}
LOG;
fwrite($sql_error_resource,$log);
fclose($sql_error_resource);
}
}
}
제껴버리고 monolog라고 하는 로깅 라이브러리를 사용해보기로 했다.
해당 라이브러리 git에 있는 readme.md 를 참조하며 개발을 진행했다.
그리고 작업하다보니 로그를 남기는 부분을 heredoc으로 작성했는데 이는 PHP 7.3부터 지원하는 것이라
7.0을 사용하는 일부 사이트에서 문제가 있어 코드를 수정했다.
/**
* @author dinist <dinist@naver.com>
* sql_result false 일때만 실행 - 쿼리 실패할때만
*
* 0 => result
* 1 => sql
* 2 => start_time
* 3 => end_time
* 4 => sql_error no & msg (relative arr)
* 5 => stacktrace in first poped..! (relative arr)
*
* 로깅 도중 array_slice로 인덱스가 하나씩 앞으로 당겨짐
*/
function sql_error_logger(...$params){
if($params[0] === false || is_null($params[0])){
$log_path = G5_DATA_PATH."/log/sql_error/";
if(!is_dir($log_path))
mkdir($log_path,G5_DIR_PERMISSION, true);
$logger = new Logger("/bbs/*");
$log_format = LoggerSetting::LOG_FORMAT;
$rotate_handler = new RotatingFileHandler($log_path."bbs.log",60,Logger::ERROR);
$rotate_handler->setFormatter(new LineFormatter($log_format,NULL,true));
$rotate_handler->setFilenameFormat(LoggerSetting::FILE_NAME_FORMAT,LoggerSetting::DATE_FORMAT);
$logger->pushHandler($rotate_handler);
$result = array_slice($params,1);
$log = "SQL : {$result[0]}".PHP_EOL;
$log .= "startTimeStamp : {$result[1]} , endTimeStamp : {$result[2]}".PHP_EOL;
$log .= "errorCode : {$result[3]['error_code']}, errorMessage : {$result[3]['error_message']}";
$logger->error($log,['file_path'=>$result[4]['file'], 'file_line'=>$result[4]['line']]);
$logger->close();
}
}
LoggerSetting은 enum 타입이 아닌 추상클래스로 만들었다 (왜 추상클래스로 만들었냐면 인스턴스화 못하게 하려고!)
enum은 8.1부터 지원하므로....
<?php
abstract class LoggerSetting{
const LOG_FORMAT = "[%level_name%][%datetime%] >> %message%".PHP_EOL."context : %context%".PHP_EOL;
const FILE_NAME_FORMAT = "{filename}-{date}";
const DATE_FORMAT = "Y-m-d";
}
LoggerSetting.php 파일은 /extend 디렉터리에 위치시킨다.
/common.php 파일을 실행하면서 /extend 디렉터리에 있는 모든 php 파일을 include 해주기 때문에 편하다.
에러 발생시 로그는 아래와 같이 기록된다.
'Web > PHP' 카테고리의 다른 글
PHP 폴더 복사 함수 코드 (0) | 2023.07.24 |
---|---|
[Laravel] - Eloquent에서 Enum 활용 (0) | 2023.05.24 |
PHP - Enum 사용하기 2 (0) | 2023.05.24 |
PHP - Enum 사용하기 (0) | 2023.05.24 |
[PhpStorm] 라라벨 serve , npm dev 설정 추가 (0) | 2023.01.17 |