데이터베이스2022. 3. 29. 14:04

mysql 또는 mariadb 에는 slow query 를 탐지해서 log 파일에 저장해주는 기능이 있다.

DB 튜닝을 위해, 속도가 느린 쿼리를 탐지해서 알려주는 기능은 참 유용한 기능이다.

 

현재, slow query 탐지 기능이 동작중인지 확인하려면, 쿼리창에 아래와 같이 실행해주면 알 수 있다.

 

show variables like 'slow_query_%';
show variables like 'long_query_%';

 

 

slow_query_log 가 OFF 상태로 동작중이지 않다.

 

탐지 기능을 켜려면,

 

mysql 이 설치된 폴더의 data 폴더아래 my.ini 설정 파일을 수정해야 한다.

 

MariaDB 10.5 버전이 설치되어 있다면, 아마도 아래와 같은 경로일 것이다.

 

C:\Program Files\MariaDB 10.5\data\my.ini

 

파일을 편집기로 열어서 아래와 같은 내용을 추가해준다.

 

slow_query_log = 1
slow_query_log_file = C:/slowlog/mysql-slow.log
long_query_time = 5

 

slow_query_log = 1

SLOW QUERY 탐지 기능을 사용한다는 의미이다.

 

slow_query_log_file = C:/slowlog/mysql-slow.log

슬로우 쿼리가 저장되는 경로와 파일명인데, 경로 규칙에 주의 해야 한다.

\ 문자가 아니라 / 문자로 폴더가 구분된다.

 

long_query_time = 5

초단위로 입력하며, 5초 이상의 쿼리를 탐지하겠다는 내용이다.

 

 

mysql 서비스를 재시작해야, 변경된 내용이 적용된다.

 

# mysql
net stop MySQL & net start MySQL
# mariadb
net stop mariadb & net start mariadb

 

윈도우의 서비스 항목에서 재시작하거나, command 창에서 재시작 해도 된다.

 

 

 

이제 변경된 설정을 확인해보면 된다.

 

show variables like 'slow_query_%';
show variables like 'long_query_%';

 

 

설정한 경로에 log 파일도 생성되었다.

 

 

슬로우쿼리 탐지가 잘 되는지 테스트 쿼리를 날려서 확인해 볼 수 있다.

 

SELECT SLEEP(5), 'slow query test.';

 

 

슬로우쿼리가 탐지될때마다, 로그 파일의 뒤에 계속 추가가 된다.

 

여기까지만 해도, 주기적으로 로그파일을 열어보고, 확인하면 되지만, 실시간으로 알림을 받을 수 있는 방법이 있다.

 

C# 의 FileSystemWatcher 를 이용하면, 파일의 내용이 변경되는걸 감지해서, slack 이나 naverworks 등의 메신저로 단체 채팅방에 알림메세지를 발송 할 수 있다.

 

 

FileSystemWatcher watcher = new FileSystemWatcher();

watcher.Filter = "mysql-slow.log";
watcher.Path = "C:\\slowlog\\";
watcher.IncludeSubdirectories = false;


watcher.NotifyFilter = NotifyFilters.DirectoryName |
   NotifyFilters.LastWrite |
   NotifyFilters.FileName |
   NotifyFilters.Size;

watcher.Changed += Watcher_Changed;
watcher.EnableRaisingEvents = true;

 

그리고, 파일에 추가되는 내용만 알림을 해야 하기 때문데, 파일 스트림의 position 을 전역변수로 기록해두자.

 

FileInfo fileinfo = new FileInfo(this.logFullPath);

if (this.streamPosition > fileinfo.Length)
{
       this.streamPosition = fileinfo.Length;
}            

 

Watcher_Changed 이벤트에서는 파일이 변경되었으니, append 된 내용만 알림을 보내면 된다.

 

using (FileStream stream = fileinfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
  stream.Position = this.streamPosition;

  using (StreamReader reader = new StreamReader(stream))
  {
    string mesageContent = reader.ReadToEnd()

    // MessageSend(messageContent);
  }
}

 

이렇게 해서 테스트를 해보면, 실시간 알림이 안오고, 한참 있다가 알림이 오게 되는걸 볼 수 있다.

 

이유는, 윈도우 시스템 (NTFS) 이 파일에 쓰기 버퍼를 사용하기 때문이다.

즉, 일정한 시간이 지나야 버퍼를 실제 물리적인 파일에 저장하기 때문이다.

 

윈도우 시스템이 실시간으로 파일을 변경하도록 하기 위해서는

로그파일을 Open / Close 를 주기적으로 해주면 된다.

 

 

try
{
  // NTFS 의 파일 캐싱으로 인해 로그파일이 변화를 watcher 가 즉시 탐지하지 못한다.
  // 파일 변화를 즉시 감지하기 위해 open 모드로 읽고 닫기를 한다.
  File.Open(this.logFullPath, FileMode.Open).Close();
}
catch (Exception)
{
}

 

try catch 를 쓰는 이유는, 로그 파일을 mysql 이 잡고 있기 때문에, Exception 이 발생하기 때문이다.

하지만, 이렇게 주기적으로 해당 파일을 건들어 주면, 윈도우 시스템이 버퍼를 파일로 저장하는 효과를 얻을 수 있어,

실시간 파일 감시가 가능해지기 때문이다.

 

이렇게 해서, 프로그램을 돌려놓고, slow query 를 발생해보면, 알림이 오는것을 확인할 수 있다.

 

 

네이버웍스 메신저르 알림을 보낸 샘플이다.

 

주의할점은 슬로우 쿼리는 트랜잭션 단위의 쿼리가 아니라, 쿼리문장 단위로 탐지를 한다는 점이다.

 

 

Posted by 헝개
개발팁2022. 3. 22. 18:46

웹API 및 Rest API 에서 swagger 는 강력한 테스트 도구이자, 문서화 도구이며,

API 를 사용하는 개발자와의 인터페이스 도구이다.

 

 

Swagger 설치 및 환경 설정

 

 

 

NuGet 패키지관리자에서 swashbuckle 을 검색해서 최신버전을 설치한다.

 

 

 

프로젝트 설정에서

웹 > 시작 작업 > 특정 페이지

 

swagger/ui/index

 

를 입력한다.

 

 

이제 F5 를 눌러 디버그를 해보면, 바로 swagger 페이지가 호출된다.

 

 

REST API 의 기본 샘플인 Values 컨트롤러에 대한 API 테스트 화면이다.

 

API 테스트 도구로서는 훌륭하지만, 문서화로써는 부족하다.

 

 

 

프로젝트 설정 > 빌드 > 출력

XML 문서 파일 체크를 한다.

 

 

 

 

 

 

App_Start > SwaggerConfig.cs 파일을 편집한다.

 

EnableSwagger 아래에 아래 라인을 추가한다.

 

c.IncludeXmlComments(string.Format(@"{0}\bin\WebApiTest.xml",
       System.AppDomain.CurrentDomain.BaseDirectory));

 

그리고, 테스트용 Api 컨트롤러 TodoController.cs 를 생성한다.

 

    public class TodoController : ApiController
    {
        // GET api/Todo
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/Todo/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/Todo
        /// <summary>
        /// Test 데이터 업로드 (dynamic by json)
        /// </summary>
        /// <remarks>
        /// {<br />
        ///     UserID: "round1",<br />
        ///     UserName: "홍길동",<br />
        ///     WorkTitle: "데이터 백업"<br />
        /// }<br />
        /// </remarks>
        /// <param name="data">
        /// json object {<br />
        /// UserID: 유저아이디<br />
        /// UserName: 유저이름<br />
        /// WorkTitle: 작업이름<br />
        /// }<br />
        /// </param>
        /// <returns></returns>
        public string Post([FromBody] dynamic data)
        {
            return "Data Posted : " + data.UserID + "=" + data.UserName + "<br />" + data.WorkTitle;
        }


        // PUT api/Todo/5
        /// <summary>
        /// PUT 데이터
        /// </summary>
        /// <remarks>
        /// {<br />
        ///     UserID: "round1",<br />
        ///     UserName: "홍길동",<br />
        ///     WorkTitle: "데이터 백업"<br />
        /// }<br />
        /// </remarks>
        /// <param name="data">
        /// json object {<br />
        /// UserID: 유저아이디<br />
        /// UserName: 유저이름<br />
        /// WorkTitle: 작업이름<br />
        /// }<br />
        /// </param>
        public string Put(int id, [FromBody] dynamic data)
        {
            return "put : " + data.WorkTitle;
        }

        // DELETE api/Todo/5
        public void Delete(int id)
        {
        }
    }

 

샘플데이터로 ValuesController 를 복사해서 수정했다.

Post / Put 메서드에 xml 주석이 들어가 있는것을 볼 수 있다.

 

프로젝트를 빌드 하면, 이 주석이 bin/WebApiTest.xml 파일로 저장이 되고,

Swagger 에서는 의 xml 을 읽어서 화면을 만들어 주게 된다.

 

 

 

TodoController 에서 메서드에 작성한 xml 주석이 swagger 에 잘 나온다.

 

xml 주석에 작성해둔 json 샘플 양식도 복사해서 붙여 넣기 하고,

Try it out! 을 누르면, 테스트도 간편하게 된다.

 

 

여기서 한가지 더 개선을 해본다면,

 

 

매번 json 샘플을 복사해서, parametar 에 붙여넣기를 해야 하는데, 이걸 자동화 해볼 것이다.

 

 

 


// Copyright 헝그리개발자(https://bemeal2.tistory.com)
// 소스는 자유롭게 사용가능합니다. Copyright 는 삭제하지 마세요.

$(document).ready(function () {

$(".toggleOperation").on('click', function (e) {

let el_content = $(e.target).closest("li.operation").find(".content");

setTimeout(function () {
if (el_content.css("display") == "block") {

let param_text = el_content.children(".markdown").text();

let el_textarea = el_content.find("textarea.body-textarea");

el_textarea.val(param_text);
let ev = new Event('input', { bubbles: true });
ev.simulated = true;

el_textarea[0].dispatchEvent(ev);

}
}, 500);

});


});

 

js 폴더를 만들고 그 아래  swager-control.js 파일을 생성한다.

 

.js 파일의 내용은 위와 같이 넣어준다.

 

 

그리고, 속성창에서 빌드작업을 [포함리소스] 로 바꿔준다.

 

SwaggerConfig.cs 파일을 열어서, EnableSwaggerUI 아래에 아래와 같이 추가해준다.

 

                .EnableSwaggerUi(c =>
                    {
                        c.InjectJavaScript(thisAssembly, "WebApiTest.js.swagger-control.js");

 

여기서 주의 할 점은 .js 파일의 경로를 지정하는 방식이다.

 

프로젝트명.폴더명.파일명  이러한 규칙으로 작성해야 한다.

 

상식적으로는 js 폴더 아래에  swagger-control.js  파일이 있으니까

"/js/swagger-control.js"  라고 해야 할것 같지만. 그렇지 않다.

 

WebApiTest 라는 프로젝트명.js라는폴더명.swagger-control.js라는파일명

 

이렇게 해서, 

"WebApiTest.js.swagger-control.js"

 

이러한 양식으로 입력해줘야 한다.

 

 

이제 swagger 페이지를 열어보면, 파라미터가 자동으로 입력된것을 볼 수 있다.

API 테스트를 위해서 Try it out! 만 눌러주면 된다!!!

 

Posted by 헝개
개발팁2022. 3. 21. 18:55

Jenkins 는 Rest API 를 통해, Project / Job 을 조회하고,

빌드하고, 결과를 확인할 수 있다.

 

 

Rest API 를 이용하면, 빌드완료후에, 빌드 결과를 메신저로 단체방에 공유 할 수 있다.

 

 

Rest API 사용하기

 

 

 

Jenkins 에 로그인하고, 오른쪽 상단에 로그인한 ID 를 클릭한다.

 

 

 

설정 메뉴를 클릭한다.

 

 

ADD NEW TOKEN 을 클릭한다.

 

 

Default name 을 입력하고, GENERATE 를 클릭하면, Token String 이 생성된다.

 

 

이 토큰문자가 REST API 를 호출할때, 인증하는 수단이 된다.

 

형식은 위에서 입력한 이름:토큰스트링 형식이다.

 

ex)

admin:12eb2d6e1d73cfa260d9e42e4d3f080b19

 

이 값은 Basic API 호출시에는 Base64 String 으로 변환해서 호출해야 한다.

간단히 fiddler 의 text wizard 에서 변환해서 쓰면 된다.

 

 

 

base64 string ex)

YWRtaW46MTJlYjJkNmUxZDczY2ZhMjYwZDllNDJlNGQzZjA4MGIxOQ==

 

 

 

Jenkins Rest API 호출

 

Job 조회 / 빌드 조회 URL 뒤에  /api/json   또는 /api/xml 만 붙여주면된다.

(소문자로 입력해야 한다.)

 

웹브라우저에서 로그인 한 상태에서, JOB 조회 화면뒤에 api/json 을 붙여보면 된다.

 

 

 

 

 

 

웹브라우저에서는 jenkins 사이트에 로그인이 된 상태에서 호출했기 때문에, 조회가 정상적으로 이뤄진다.

curl 명령어로 command prompt 에서 실행해보자.

 

 

curl -X GET "http://url:port/job/BUILD/job/BUILD_CLIENT_FOR_Develop/api/json" -H "Content-Type: application/json"

 

오류가 날 것이다. 아래와같이 Basic Authorization 을 추가하여 호출해보자.

 

curl -X GET "http://url:port/job/BUILD/job/BUILD_CLIENT_FOR_Develop/api/json" -H "Content-Type: application/json" -H "Authorization: Basic YWRtaW46MTJlYjJkNmUxZDczY2ZhMjYwZDllNDJlNGQzZjA4MGIxOQ=="

 

 

결과가 잘 나올 것이다.

 

 

 

Jenkins 결과 확인 API

 

빌드 결과 확인 API
{jobUrl}{buildNumber}/api/json

빌드 결과 Console 확인 API
{jobUrl}{buildNumber}/consoleText

 

 

Jenkins 빌드 후 조치

 

 

 

Jenkins 의 job 설정에서 빌드 후 조치

 

Execute Windows batch command 를 통해서, 빌드완료후에, 빌드결과 알림 Console Application 을 호출할것이다.

 

jenkins_build_noti.bat

 

파일과, 그 안에서 호출하는 Application 을 만들어야 한다.

 

Application 은 Visual Studio 에서 Console Application 으로 만들면 된다.

 

 

 

jenkins_build_noti.bat

 

D:\경로\Util.NaverWorks.BuildNoti.exe "jenkins" "%JOB_URL%" "%BUILD_NUMBER%"

 

jenkins 빌드가 완료되면, 전역변수

 

%JOB_URL% 에 현재 빌드된 job 의 url 이 자동으로 입력되고,

%BUILD_NUMBER% 에는 빌드된 빌드번호가 자동으로 입력된다.

 

 

 

Console Application

 

main 함수

            if (args.Length >= 1)
            {
                using (NaverWorksNoti noti = new NaverWorksNoti())
                {
                    switch (args[0])
                    {
                        case "jenkins":
                            noti.SendJenkins(args[1], args[2]);
                            break;
                        default:
                            WriteUsage();
                            break;
                    }

                    return 0;   // always return success
                }

 

여기서 주의 할 점은 return 0 이다. 0 이 아닌 값을 리턴하면,  jenkins 는 빌드가 실패한걸로 처리하게 된다.

 

 

SendJenkins 함수

string url = $"{jobUrl}{buildNumber}/api/json";

 

using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

client.DefaultRequestHeaders.Add("Authorization", "Basic YWRtaW46MTJlYjJkNmUxZDczY2ZhMjYwZDllNDJlNGQzZjA4MGIxOQ==");

HttpResponseMessage response = client.GetAsync(url).Result;
string result = response.Content.ReadAsStringAsync().Result;

dynamic objResult = JsonConvert.DeserializeObject(result);

........

}

 

이제 objResult 라는 json 객체에서 빌드 성공/실패 여부를 판단하고, 알림 메세지를 만들어 내면 된다.

 

bool buildSuccess = objResult.result.ToString() == "SUCCESS";

 

result 값이 SUCCESS 라면 빌드성공 아니면 실패로 처리하면 된다.

 

actions 의 parameters 를 통해 job 빌드시 사용한 파라미터를 가져올 수 도 있다.

 

                    for (int i = 0; i < objResult.actions.Count; i++)
                    {
                        if (objResult.actions[i].parameters != null)
                        {
                            for (int k = 0; k < objResult.actions[i].parameters.Count; k++)
                            {
                                if (objResult.actions[i].parameters[k].value != null && objResult.actions[i].parameters[k].value.ToString() != "")
                                {
                                        
                                    sbParameter.Append($"{objResult.actions[i].parameters[k].name.ToString()} : {objResult.actions[i].parameters[k].value.ToString()}");

sbParameter.AppendLine();
                                }
                            }
                        }
                    }

 

svn 연동해서, 빌드하는 경우, changeSet 을 통해서, svn 커밋내용들을 가져올 수 있다.

 

if (objResult.changeSet.items != null)
{
for (int i = 0; i < objResult.changeSet.items.Count; i++)
{
sbMessage.AppendLine($"{objResult.changeSet.items[i].user.ToString()} : {(objResult.changeSet.items[i].date == null ? "" : Convert.ToDateTime(objResult.changeSet.items[i].date).ToLocalTime().ToString())}");
sbMessage.Append(objResult.changeSet.items[i].msg.ToString());

sbMessage.AppendLine();
}
}

 

빌드가 실패한경우에는 consoleText 를 보여줘서, 오류를 확인하도록 할 수도 있다.

 

string consoleUrl = $"{jobUrl}{buildNumber}/consoleText";

 

response = client.GetAsync(consoleUrl).Result;
byte[] resultBytes = response.Content.ReadAsByteArrayAsync().Result;

// EUC-KR 인코딩
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
string resultText = Encoding.GetEncoding(51949).GetString(resultBytes);

sbMessage.Append(resultText);

 

 

이렇게 단체방에 전송할 메세지를 만들어서 단체방 메세지를 보낼 수 있다.

 

 

 

알림 메세지 예제

 

아래 예제는 네이버웍스에 Bot 을 생성해서, 단체방에 메세지를 보낸 화면이고, api 가 제공된다면 다른 메신저로 보낼 수도 있다.

 

Posted by 헝개