Notice
Recent Posts
Recent Comments
Link
«   2026/06   »
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
Tags
more
Archives
Today
Total
관리 메뉴

ssuperjun 님의 블로그

[과제5-4] DB 모니터링 프로그램 Python 변환 4편: Python 코드 작성 본문

인턴

[과제5-4] DB 모니터링 프로그램 Python 변환 4편: Python 코드 작성

ssuperjun 2026. 1. 17. 14:51

[설계 계획]

C의 전역 변수, 구조체 → Python 클래스, 전역 리스트, dataclass

pthread → threading.Thread

signal → signal, time.sleep

MySQL C API → pymysql (prepared statement 유사 지원, bind 없이 바로 execute 가능)

로그 파일 → logging 또는 open(..., "a")

C스타일 error handling (goto, label) → try-except-finally

 

[코드 순서]

- import 및 전역 상수 정의

- @dataclass class DBHost

- init_table(user_id) -> List[DBHost]

- thread_main(user_id)

- thread_process_list(thread_idx, db_host_slice, log_db_conn)

- main()

 

디버그 및 예외 추적을 용이하게 하기 위해, try 블록 안의 코드 블록을 길게 만들지 않기


[고려한 점]

사용자 ID에 해당하는 서버 목록을 DB에서 불러오는 부분에서, C코드는 실패 시 segfault를 발생시켰지만

Python 코드는 segfault를 방지하고 실패하더라도 에러 로그를 파일에 남기기 위해, C 코드와는 실행 순서를 달리 함

 

[코드]

# process2.py

import sys
import signal
import pymysql
import threading
import time
import datetime
import logging
from dataclasses import dataclass
from typing import List

# ---------- 설정값 ----------
# MyMON DB 접속 정보
# 모니터링 대상 서버 목록이 저장된 일종의 메타 DB
MYMON_HOST    = "10.생략" # 유출 안되게 주의
MYMON_USER    = "admin_2026"
MYMON_PASSWD  = ""
MYMON_DB      = "MYMON"
MYMON_PORT    = 13306

# 로그 DB 접속 정보
# 수집된 정보를 기록하는 DB
MYSQL_LOG_HOST    = "10."
MYSQL_LOG_USER    = "admin_2026"
MYSQL_LOG_PASSWD  = ""
MYSQL_LOG_DB      = "log"
MYSQL_LOG_PORT    = 13306

# ServiceDB(수집 대상 서버) 접속 정보
# ThreadProcessList() 내부에서 각 dbHost[i]에 접속할 때 사용됨. dbHost[i]는 InitTable()에서 선언된 DB 호스트 정보 배열
MYSQL_USER   = "admin"
MYSQL_PASSWD = ""

MYSQLHOST_NUM     = 2000 # 감시할 서버 최대 개수
INTERVALTIME_SEC  = 1 # 수집 주기 (1초)
THREADCOUNT       = 15 # 최대 스레드 개수

HOSTNAME_LEN = 25
IP_LEN = 15
LOGFILE_LEN = 18

# ---------- 디버그 모드 ----------
DEBUG = False  # 필요 시 True로 설정

# ---------- 구조체 ----------
@dataclass
class dbHost_struct:
    host: str
    port: int
    ip: str
    service: str

@dataclass
class Interval_Server:
    nStart: int
    nEnd: int

# ---------- 전역 변수 ----------
dbHost: List[dbHost_struct] = []  # 감시할 서버 정보 리스트
g_IntervalServer: List[Interval_Server] = []  # 스레드별 서버 인덱스 범위

g_nServerCnt = 0  # 감시할 서버 개수
g_strCurrTime = ""  # 수집 시간 (로그 timestamp)
g_strCurrDate = ""
g_strPrevTime = ""

g_nCount = 0  # 수집 횟수

strUserID = ""  # 사용자 ID (argv[1]에 해당)

g_fCheckLog = None  # 로그 파일 핸들

g_nTimeout = 2  # MySQL 접속 타임아웃 (초)

# ---------- 로그 설정 ----------
logging.basicConfig(
    filename='monitor.log',
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s'
)

# ---------- 시간 문자열 생성 ----------
def get_time_strings():
    now = datetime.datetime.now()
    g_strCurrTime = now.strftime('%Y%m%d%H%M%S')
    g_strCurrDate = now.strftime('%Y%m%d')
    return g_strCurrTime, g_strCurrDate

# 감시(모니터링)할 서버 목록을 MyMon DB에서 조회하여 dbHost 배열에 저장
# ThreadMain 함수 첫부분에 한번만 등장할 예정
def InitTable() -> int:
    global dbHost, g_nServerCnt, strUserID, g_mysql

    strQuery = (
        "SELECT host, "
        "       port, "
        "       SUBSTRING_INDEX(ip, '|', -1), "
        "       service_detail "
        "  FROM MYMON.serverinfo B, MYMON.admin_user C "
        " WHERE B.status = 1 "
        "   AND B.db_charged = CONVERT(C.admin_name USING euckr) "
        "   AND MONITORING <> '' "
        "   AND repl_state != 'MM' "
        f"  AND C.kerberos_id = '{strUserID}' "
        " ORDER BY 1"
    )

    # MyMON DB connect
    try:
        g_mysql = pymysql.connect(
            host=MYMON_HOST,
            user=MYMON_USER,
            password=MYMON_PASSWD,
            database=MYMON_DB,
            port=MYMON_PORT,
            charset='utf8',
            connect_timeout=g_nTimeout
        )
    except pymysql.MySQLError as e:
        print(f"[InitTable] Failed to connect to database: Error: {e}")
        return -1

    # Cursor create
    # 커서: 쿼리 결과를 순회하고 조작하는 객체
    try:
        cursor = g_mysql.cursor()
    except Exception as e:
        print(f"[InitTable] Failed to create cursor: Error: {e}")
        g_mysql.close()
        return -1

    # SET NAMES
    # UTF-8 문자셋 설정
    try:
        cursor.execute("SET NAMES utf8")
    except Exception as e:
        print(f"[InitTable] Failed to set charset: Error: {e}")
        cursor.close()
        g_mysql.close()
        return -1

    # Execute query
    try:
        cursor.execute(strQuery)
        rows = cursor.fetchall()
    except Exception as e:
        print(f"[InitTable] Failed to query database item: Error: {e}")
        cursor.close()
        g_mysql.close()
        return -1

    cursor.close()
    g_mysql.close()

    # Result check
    # 쿼리 실행은 됐지만, 쿼리 결과가 없는 경우
    if not rows:
        print("[InitTable] Select Server Error: result is empty")
        return -1

    # 쿼리 결과를 받아서, 각 레코드를 순회하며 dbHost[] 배열에 저장
    # 이 배열은 이후 ThreadProcessList 함수에서 수집 루프에 사용됨
    dbHost.clear()
    for row in rows:
        dbHost.append(
            dbHost_struct(
                host=row[0],
                port=int(row[1]),
                ip=row[2],
                service=row[3]
            )
        )

    g_nServerCnt = len(dbHost)
    return 0

# 핵심 수집 실행 로직
# 각 스레드별로 수집 대상 서버 목록을 InitTable 함수에서 받아와서, 수집 작업을 병렬 처리한 뒤 결과를 LogDB에 기록
# InitTable 함수 1번 호출, ThreadProcessList 함수 반복 호출됨
def ThreadMain(dummy_arg=None):
    global g_IntervalServer, g_nServerCnt, dbHost
    global g_strCurrTime, g_strCurrDate, g_nCount, g_fCheckLog

    # 현재 시간 문자열 준비
    now = datetime.datetime.now()
    g_strCurrTime = now.strftime("%Y%m%d%H%M%S")
    g_strCurrDate = now.strftime("%Y%m%d")

    # 로그 파일명 생성
    strFileNameLog = f"./log/_myproc_{strUserID}.log.{now.strftime('%Y%m%d')}"

    # 스크립트 실행 디렉토리 안에 log 디렉토리가 없으면 에러
    try:
        g_fCheckLog = open(strFileNameLog, "a")
    except Exception as e:
        print(f"Cannot create log file Error! {e}")
        exit(-1)

    # 서버 목록 초기화
    # 사용자 ID에 해당하는 서버 목록을 DB에서 불러옴 - dbHost[], g_nServerCnt
    # 이 부분 실패 시 에러 로그를 파일에 남기기 위해, C 코드와는 실행 순서를 달리 함 -> segmentation fault(segfault) 방지
    if InitTable() == -1:
        print("### MyMon ServerDB tb_serverinfo Table Error!! ###")
        g_fCheckLog.write(f"{now.strftime('%Y-%m-%d %H:%M:%S')}, MyMon ServerDB tb_serverinfo Table Error!!\n")
        g_fCheckLog.close()
        exit(-1)

    # 스레드 수 결정
    if g_nServerCnt > THREADCOUNT:
        nThreadCount = THREADCOUNT
    else:
        nThreadCount = 1

    nIntervalCount = g_nServerCnt // nThreadCount

    # 스레드별 할당 범위 설정
    g_IntervalServer = [Interval_Server(0, 0) for _ in range(nThreadCount)]

    for i in range(nThreadCount):
        start_idx = i * nIntervalCount
        end_idx = start_idx + nIntervalCount - 1
        if i == nThreadCount - 1:  # 마지막 스레드는 나머지 포함
            end_idx = g_nServerCnt - 1
        g_IntervalServer[i] = Interval_Server(nStart=start_idx, nEnd=end_idx)

    # 실행 시간 측정 시작
    start_time = time.time()

    threads = []

    for i in range(nThreadCount):
        t = threading.Thread(target=ThreadProcessList, args=(i,))
        threads.append(t)
        t.start()

    for t in threads:
        t.join()

    # 실행 시간 측정 종료
    end_time = time.time()
    dElapsedTime = end_time - start_time
    per_server = dElapsedTime / g_nServerCnt if g_nServerCnt > 0 else 0

    # 로그 기록
    log_line = f"\n{now.strftime('%Y-%m-%d %H:%M:%S')}, Server:{g_nServerCnt}, Elapsed:{dElapsedTime:.3f}, perServer:{per_server:.3f}\n"
    print(log_line.strip())
    g_fCheckLog.write(log_line)
    g_fCheckLog.close()

# 스레드별로 각자 맡은 대상 서버에 접속해 정보 수집, 로그DB에 기록
def ThreadProcessList(nOrder: int):
    global dbHost, g_IntervalServer, g_strCurrTime, g_strCurrDate, g_nCount

    # Log DB 연결
    try:
        log_db = pymysql.connect(
            host=MYSQL_LOG_HOST,
            user=MYSQL_LOG_USER,
            password=MYSQL_LOG_PASSWD,
            database=MYSQL_LOG_DB,
            port=MYSQL_LOG_PORT,
            charset='utf8',
            cursorclass=pymysql.cursors.Cursor,
            connect_timeout=g_nTimeout
        )
    except pymysql.MySQLError as e:
        print(f"Failed to connect to meta database: Error: {e}")
        time.sleep(1)
        return

    # 서버 범위 가져오기
    nStart = g_IntervalServer[nOrder].nStart
    nEnd = g_IntervalServer[nOrder].nEnd

    for i in range(nStart, nEnd + 1):
        host = dbHost[i].host
        port = dbHost[i].port
        ip = dbHost[i].ip
        service = dbHost[i].service

        # 대상 서버 접속 시도
        try:
            svc_db = pymysql.connect(
                host=host,
                user=MYSQL_USER,
                password=MYSQL_PASSWD,
                database='mysql',
                port=port,
                charset='utf8',
                cursorclass=pymysql.cursors.Cursor,
                connect_timeout=g_nTimeout
            )
        except pymysql.MySQLError as e:
            if port != 0:
                print(f" > [{host}:{port}], Failed to connect to database: Error: {e}")
            continue

        # 특정 서버 제외 (ifsr-dev*)
        if host.startswith("ifsr-dev"):
            svc_db.close()
            continue

        try:
            # 쿼리 실행: processlist, innodb status, version, innodb lock
            # ================================
            # SHOW FULL PROCESSLIST
            # ================================
            cursor = None
            try:
                cursor = svc_db.cursor() # 대상 서버 커서(쿼리 결과를 순회하는 객체) 생성
            except Exception as e:
                print(f" * processList cursor create failed: {host} {port}")
                print(f" * MySQL Error Message: {e}")
                svc_db.close()
                continue

            try:
                cursor.execute("SHOW FULL PROCESSLIST") # C 코드에선 strProcess 문자열을 사용했으나, 여기선 바로 쿼리문을 넣음. 가독성
                result = cursor.fetchall()
            except Exception as e:
                print(f" * Query processList Result result is null: {host} {port}")
                print(f" * MySQL Error Message: {e}")
                cursor.close()
                svc_db.close()
                continue # goto QUERYERROR;과 같은 효과

            if not result:
                print(f" * processList mysql_num_rows is Zero: {host} {port}")
                cursor.close()
                svc_db.close()
                continue

            for row in result:
                # row mapping (SHOW FULL PROCESSLIST)
                # 0: Id, 1: User, 2: Host, 3: db, 4: Command, 5: Time, 6: State, 7: Info

                # ----------------
                # Filter conditions (C 코드 그대로)
                # ----------------
                if (
                    (row[1] == "system user" and (row[6] or "").startswith("Waiting for master")) or
                    row[1] == "event_scheduler" or
                    (row[4] == "Sleep" and int(row[5] or 0) < 20000) or
                    row[4] == "Binlog Dump"
                ):
                    continue

                # ----------------
                # 값 매핑
                # ----------------
                str_logtime = g_strCurrTime
                str_logdate = g_strCurrDate
                str_host = host
                nPort = port
                str_Pid = str(row[0] or "0")
                str_user = row[1] or "NULL"

                # ip:port → ip 분리
                str_ip = row[2] or "NULL"
                if ":" in str_ip:
                    str_ip = str_ip.split(":")[0]

                str_db = row[3] or "NULL"
                str_command = row[4] or "NULL"
                str_ExecTime = str(row[5] or "0")
                str_state = row[6] or "NULL"
                str_query = row[7] or "NULL"
                nCount = g_nCount

                # ----------------
                # Warning log 출력 (C 코드 조건 그대로)
                # ----------------
                if (
                    (str_state.startswith("Locked") and int(str_ExecTime) > 5) or
                    str_state.startswith("Killed") or
                    (str_state.startswith("Sending") and int(str_ExecTime) > 5) or
                    (str_state.startswith("Waiting") and int(str_ExecTime) > 5) or
                    (str_command.startswith("Query") and int(str_ExecTime) > 5) or
                    (str_state.startswith("Sorting") and int(str_ExecTime) > 5)
                ):
                    warn_msg = (
                        f"\n {str_host}:{nPort} [{service}] -  "
                        f"State: {str_state}, Time: {str_ExecTime}, "
                        f"User: {str_user}@{str_ip}\n"
                        f" DataBase: {str_db}\n"
                        f" Query : {str_query}\n"
                    )
                    print(warn_msg, end="")
                    if g_fCheckLog:
                        g_fCheckLog.write(warn_msg)

                # ----------------
                # INSERT INTO processlist
                # ----------------
                try:
                    log_cursor = log_db.cursor()
                except Exception as e:
                    print(f" * processlist insert cursor error: {e}")
                    continue

                try:
                    log_cursor.execute(
                        """
                        INSERT INTO processlist
                        VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,COMPRESS(%s),%s)
                        """,
                        (
                            str_logtime,
                            str_logdate,
                            str_host,
                            nPort,
                            str_Pid,
                            str_user,
                            str_ip,
                            str_db,
                            str_command,
                            str_ExecTime,
                            str_state,
                            str_query,
                            nCount,
                        ),
                    )
                    log_db.commit()
                except Exception as e:
                    print(f" * processlist insert failed: {host} {port}")
                    print(f" * MySQL Error Message: {e}")
                finally:
                    log_cursor.close()
                    cursor.close()

            # ================================
            # SHOW ENGINE INNODB STATUS
            # ================================
            try:
                cursor = svc_db.cursor()
                cursor.execute("SHOW ENGINE INNODB STATUS")
                result = cursor.fetchall()
            except Exception as e:
                print(f" * InnoDB Status query failed: {host} {port}")
                print(f" * MySQL Error Message: {e}")
                cursor.close()
                continue

            if not result:
                print(f" * InnoDB Status result is empty: {host} {port}")
                cursor.close()
                continue

            for row in result:
                # ----------------
                # 버전별 InnoDB Status 위치 분기
                # ----------------
                if row[0] and row[0].startswith("InnoDB"):
                    # MySQL 5.1+
                    str_status = row[2] if row[2] else "NULL"
                    nFlag = 1
                else:
                    # MySQL 5.0
                    str_status = row[0] if row[0] else "NULL"
                    nFlag = 0

                str_logtime = g_strCurrTime
                str_logdate = g_strCurrDate
                str_host = host
                nPort = port

                # ----------------
                # INSERT INTO innodb
                # ----------------
                try:
                    log_cursor = log_db.cursor()
                except Exception as e:
                    print(f" * innodb insert cursor error: {e}")
                    cursor.close()
                    continue

                try:
                    log_cursor.execute(
                        """
                        INSERT INTO innodb
                        VALUES (%s, %s, %s, %s, COMPRESS(%s))
                        """,
                        (
                            str_logtime,
                            str_host,
                            nPort,
                            str_status,
                        ),
                    )
                    log_db.commit()
                except Exception as e:
                    print(f" * innodb insert failed: {host} {port}")
                    print(f" * MySQL Error Message: {e}")
                finally:
                    log_cursor.close()
                    cursor.close()

            # ================================
            # SHOW MYSQL VERSION
            # ================================
            try:
                cursor = svc_db.cursor()
                cursor.execute("SELECT VERSION()")
                result = cursor.fetchone()
            except Exception as e:
                print(f" * VERSION query failed: {host} {port}")
                print(f" * MySQL Error Message: {e}")
                cursor.close()
                continue

            if not result:
                print(f" * VERSION result is empty: {host} {port}")
                cursor.close()
                continue

            str_version = result[0]
            cursor.close()

            # ----------------
            # MySQL 버전 파싱
            # ----------------
            # 예: "5.7.35-log" or "8.0.32"
            if str_version.startswith("5.7") or str_version.startswith("5.6"):
                str_lock_query = """
                    SELECT r.trx_mysql_thread_id as waiting_thread,
                        r.trx_id as waiting_trx_id,
                        r.trx_query as waiting_query,
                        b.trx_mysql_thread_id as blocking_thread,
                        b.trx_id as blocking_trx_id,
                        b.trx_query as blocking_query
                    FROM information_schema.innodb_lock_waits w
                    JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
                    JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id
                """
            elif str_version.startswith("8.0"):
                str_lock_query = """
                    SELECT r.trx_mysql_thread_id AS waiting_thread,
                        r.trx_id AS waiting_trx_id,
                        r.trx_query AS waiting_query,
                        b.trx_mysql_thread_id AS blocking_thread,
                        b.trx_id AS blocking_trx_id,
                        b.trx_query AS blocking_query
                    FROM performance_schema.data_lock_waits dw
                    JOIN information_schema.innodb_trx r ON r.trx_id = dw.requesting_trx_id
                    JOIN information_schema.innodb_trx b ON b.trx_id = dw.blocking_trx_id
                """
            else:
                print(f" * MySQL Version [{str_version}] not supported for LOCK analysis: {host} {port}")
                continue
            
            # ================================
            # SHOW INNODB LOCK
            # ================================
            
            if nFlag == 0: # MySQL 5.0 이하는 LOCK 분석 스킵
                continue

            try:
                cursor = svc_db.cursor()
                cursor.execute(str_lock_query)
                result = cursor.fetchall()
            except Exception as e:
                print(f" * InnoDB Lock query failed: {host} {port}")
                print(f" * MySQL Error Message: {e}")
                cursor.close()
                continue

            if not result:
                print(f" * InnoDB Lock result is empty: {host} {port}")
                cursor.close()
                continue

            for row in result:
                str_logtime = g_strCurrTime
                str_logdate = g_strCurrDate
                str_host = host
                nPort = port

                try:
                    log_cursor = log_db.cursor()
                except Exception as e:
                    print(f" * innodb_lock insert cursor error: {e}")
                    continue

                try:
                    waiting_thread = int(row[0]) if row[0] else 0
                    waiting_trx_id = row[1] or "0"
                    waiting_query = row[2] or "0"
                    blocking_thread = int(row[3]) if row[3] else 0
                    blocking_trx_id = row[4] or "0"
                    blocking_query = row[5] or "0"

                    log_cursor.execute(
                        '''
                        INSERT INTO innodb_lock
                        VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
                        ''',
                        (
                            str_logtime,
                            str_logdate,
                            str_host,
                            nPort,
                            waiting_trx_id,
                            waiting_thread,
                            0,  # waiting_time (optional)
                            waiting_query,
                            blocking_trx_id,
                            blocking_thread,
                            0,  # blocking_time (optional)
                            blocking_query,
                        )
                    )
                    log_db.commit()
                except Exception as e:
                    print(f" * innodb_lock insert failed: {host} {port}")
                    print(f" * MySQL Error Message: {e}")
                finally:
                    log_cursor.close() # log_db 커서 종료

            cursor.close() # 대상 서버 커서 종료
        finally: # 예외 발생 여부와 상관없이 항상 접속 종료
            svc_db.close() # 대상 서버 접속 종료

    log_db.close() # Log DB 접속 종료

# 시그널 핸들러: SIGALRM 수신 시 ThreadMain 호출
def signal_handler(signum, frame):
    global g_nCount
    ThreadMain(1)
    g_nCount += 1

# 프로그램 진입점. signal handler 설정, 주기적 호출 준비
# ThreadMain 함수 호출
# 실행 명령어 python process2.py admin01 실행 시, argv[1]은 "admin01" 
def main():
    global strUserID, g_nCount

    if len(sys.argv) != 2:
        print("Usage: python process2.py <admin_id>")
        sys.exit(1)

    strUserID = sys.argv[1]

    # 최초 1회 수집 수행
    ThreadMain(1)

    # 디버그 모드: 무한히 직접 호출
    if DEBUG:
        while True:
            ThreadMain(1)
            g_nCount += 1
            time.sleep(INTERVALTIME_SEC) # C 코드에선 sleep 없이 바로 호출했으나, 여기선 약간의 지연 추가
        return

    # 시그널 핸들러 등록
    signal.signal(signal.SIGALRM, signal_handler)

    while True:
        # INTERVALTIME_SEC 초 후 SIGALRM 발생 예약
        signal.alarm(INTERVALTIME_SEC)
        signal.pause()  # SIGALRM 대기

if __name__ == "__main__":
    main()

 

[프롬프트]

C 코드를 Python으로 변환하고 싶어. process2.c를 process2.py로 바꿔줘. process2.c는 모니터링 프로그램이고 다음과 같은 특징이 있어.
여러 MySQL 서버에 접속해 아래의 정보를 주기적으로 수집하고, 로그 DB에 기록

[주요 기능]
특정 관리자의 책임 서버 목록을 조회 (DB에서)
각 서버의 SHOW PROCESSLIST, SHOW ENGINE INNODB STATUS, INNODB LOCK 정보를 수집
쓰레드로 병렬 수집 (최대 15개 쓰레드)
수집된 결과를 로그 파일과 DB에 저장
수집 주기는 INTERVALTIME_SEC (1초로 설정해둠)

주요 흐름 요약
1. 실행 (main)
→ 사용자 ID(argv[1])을 인자로 받아 관리 대상 서버 목록 조회 준비
2. InitTable
→ MYMON DB에서 admin_user와 serverinfo 테이블을 조인하여 대상 서버 정보 획득
→ 구조체 배열 dbHost[]에 저장
3. ThreadMain
→ 현재 시간 기반 로그 파일 생성
→ 수집 대상 서버를 쓰레드 수에 맞게 분할
→ pthread_create()로 ThreadProcessList() 실행
4. ThreadProcessList
→ 각 서버에 대해:
* SHOW FULL PROCESSLIST → 비정상 쿼리/긴 쿼리 감지 및 로깅
* SHOW ENGINE INNODB STATUS → InnoDB 내부 상태 수집
* INNODB_LOCK_WAITS, INNODB_TRX → 락 대기 정보 수집
→ 모든 결과는 log DB에 insert (stmt 방식 + compress)

[함수별 내용] 
[main 함수] 
프로그램 진입점. signal handler 설정, 주기적 호출 준비 
[흐름] 
사용자 ID(argv[1])를 strUserID에 저장 
시그널 핸들러 설정: SIGALRM 시 ThreadMain 함수 호출 
무한 루프: SIGALRM 시그널을 주기적으로 발생시켜 ThreadMain 호출 
디버그 모드일 경우, 무한 루프에서 ThreadMain을 계속 호출하고 g_nCount 증가로 호출 횟수 추적 

[InitTable 함수] 
감시(모니터링)할 서버 목록을 MyMon DB에서 조회하여 dbHost 배열에 저장 
[흐름] 
사용자 ID(strUserID)에 해당하는 감시 대상 서버 목록 조회 쿼리 저장 
MyMon DB에 연결 
앞서 만든 쿼리를 실행 
쿼리 실행 성공한 경우, 쿼리 결과를 받아서, 각 레코드를 순회하며 dbHost[] 배열에 저장. 이 배열은 이후 ThreadProcessList 함수에서 수집 루프에 사용됨 
쿼리 실행은 됐지만, 쿼리 결과가 없을 경우 에러 핸들링 
 
[ThreadMain 함수] 
각 스레드별로 수집 대상 서버 목록을 InitTable 함수에서 받아와서, 수집 작업(ThreadProcessList 함수)을 병렬 처리한 뒤 결과를 LogDB에 기록 
[흐름] 
InitTable 함수를 호출해 서버 목록 초기화(사용자 ID에 해당하는 서버 목록을 DB에서 불러옴 - dbHost[], g_nServerCnt) 
현재 시간 정보를 얻어와서 로그 파일 이름 생성 
스레드 생성 개수: 감시할 서버 개수가 많다면 THREADCOUNT(15)개, 적다면 1개 
스레드별로 수집할 서버 범위 설정 
Thread Run: 각 스레드는 ThreadProcessList 함수 실행, g_IntervalServer[i] 범위의 서버들을 수집 처리 
Thread Wait: 모든 스레드가 종료될 때까지 대기 
수행시간 등 로그 파일에 기록 
리소스 해제 

[ThreadProcessList(void *arg) 함수] 
[코드 흐름] 
변수들 미리 선언 
스레드별로 각자 맡은 대상 서버에 접속해 정보 수집, 로그DB에 기록 
Show processlist, SHOW ENGINE INNODB STATUS, SHOW MYSQL VERSION, SHOW INNODB LOCK 구현 부분은 분량이 많지만, 흐름 자체가 동일했음 

[구체적인 흐름] 
스레드가 대상 서버 접속에 성공한 경우 
에러 처리: 쿼리 실행 실패 경우, 쿼리 결과가 null인 경우, 쿼리 결과는 정상적으로 받아왔지만, row 수가 0인 경우 
쿼리 결과를 정상적으로 가져왔다면, 각 row(쿼리 실행 결과의 row 하나는 곧 프로세스 하나의 레코드를 의미)별로 처리 
stmt과 bind를 사용하여 쿼리 준비, 실행 

[참고] 
SHOW INNODB LOCK 구현부의 경우, 실행할 InnoDB Lock 쿼리는 SHOW MYSQL VERSION 구현부에서 생성, 저장됨