이번 코드는 Python 표준 logging 모듈을 이용해 에이전트의 실행 과정을 기록하는 기초 실습입니다.
앞서 만들었던 Weather Agent에 연결해서 생각하면 이해하기 쉽습니다. 사용자가 서울 날씨를 물어보았을 때, 에이전트가 어떤 함수를 호출했는지, 어떤 지역을 지원하는 것으로 판단했는지, 정상적으로 데이터를 반환했는지, 지원하지 않는 지역이라 경고가 발생했는지 등을 app.log 파일에 기록하는 것입니다.
이번 실습에서는 다음 순서로 진행하겠습니다.
-
코드가 어떤 역할을 하는지 이해하기
-
테스트용 Python 파일 만들기
-
정상 입력과 잘못된 입력으로 테스트하기
-
app.log 파일의 로그 한 줄씩 해석하기
-
로그 레벨을 INFO, DEBUG, WARNING, ERROR로 바꾸어 비교하기
-
현재 코드에서 보완할 점 확인하기
1. 먼저 전체 실행 구조 이해하기

핵심은 logging이 날씨를 조회하는 기능 자체를 수행하는 것이 아니라, 함수의 실행 과정과 상태를 기록하는 역할을 한다는 점입니다.
2. 로깅 설정 코드 이해하기
import loggingfrom typing
import Dict, Union
-
logging: Python에 기본 내장된 표준 로깅 모듈입니다. 별도로 설치할 필요가 없습니다.
-
Dict, Union: 함수가 반환하는 데이터의 자료형을 표시하기 위한 타입 힌트입니다.
이제 가장 중요한 설정 부분입니다.
logging.basicConfig(
level=logging.INFO,
filename="app.log",
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
)
각 항목을 하나씩 살펴보겠습니다.
| 항목 |
의미 |
| level=logging.INFO | INFO 이상 레벨의 로그만 기록 |
| filename="app.log" | 로그를 app.log 파일에 저장 |
| format | 로그 한 줄의 출력 형식 지정 |
format은 다음과 같이 구성됩니다.
%(asctime)s - %(levelname)s - %(name)s - %(message)s
예를 들어 실제 로그가 다음과 같이 기록되었다고 가정해 보겠습니다.
2026-11-08 14:30:00,123 - INFO - __main__ - get_current_weather 호출: location=서울
| 구성 요소 | 실제 값 | 의미 |
| asctime | 2026-11-08 14:30:00,123 | 로그가 발생한 시각 |
| levelname | INFO | 로그의 중요도 |
| name | __main__ | 로거 이름 |
| message | get_current_weather 호출... | 실제 기록 내용 |
즉, 이 설정은 로그를 단순한 문장으로 저장하는 것이 아니라 언제, 어떤 수준의 중요도로, 어떤 모듈에서, 무슨 일이 일어났는지 구조적으로 기록합니다.

3. 로거(Logger) 생성하기
logger = logging.getLogger(__name__)
이 코드는 로그를 기록할 때 사용할 로거 객체를 생성합니다.
여기서 __name__은 현재 Python 모듈의 이름입니다.
예를 들어 파일 이름이 weather_logging_test.py이고 직접 실행한다면 다음과 같습니다.
print(__name__)
출력:
__main__
따라서 로그에는 다음처럼 기록됩니다.
INFO - __main__ - get_current_weather 호출: location=서울
나중에 이 코드를 ADK 프로젝트의 weather_agent.py 같은 모듈에서 import하여 사용하면 로거 이름은 모듈의 패키지 경로에 따라 달라집니다.
이렇게 모듈 이름을 로거에 포함하면 여러 Agent와 Tool이 실행될 때 어느 모듈에서 로그가 발생했는지 구분할 수 있습니다.
4. 로그 레벨 5가지 이해하기
Python 표준 로깅에는 다음과 같은 로그 레벨이 있습니다.
| 레벨 |
숫자값 | 의미 | 일반적인 사용 |
| DEBUG | 10 | 상세한 디버깅 정보 | 함수 내부 처리 과정 |
| INFO | 20 | 정상적인 실행 정보 | 함수 호출, 처리 완료 |
| WARNING | 30 | 주의가 필요한 상황 | 지원하지 않는 지역 요청 |
| ERROR | 40 | 오류 발생 | 처리 실패 |
| CRITICAL | 50 | 매우 심각한 오류 | 시스템을 계속 실행하기 어려운 상황 |
중요한 원칙이 있습니다.
level=logging.INFO로 설정하면 INFO보다 낮은 DEBUG는 기록하지 않고, INFO와 그보다 높은 WARNING, ERROR, CRITICAL은 기록합니다.
현재 코드의 설정은 다음과 같습니다.
logging.basicConfig(
level=logging.INFO,
filename="app.log",
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
)
따라서 현재 코드에서 다음 로그들은 이렇게 처리됩니다.
| 코드 |
로그 레벨 | 현재 설정에서 기록? |
| logger.debug() | DEBUG | 아니요 |
| logger.info() | INFO | 예 |
| logger.warning() | WARNING | 예 |
| logger.error() | ERROR | 예 |
개발 중에는 DEBUG까지 확인하고 싶을 수 있습니다. 그럴 때는 다음과 같이 바꾸면 됩니다.
level=logging.DEBUG
그러면 모든 5개 레벨이 기록됩니다.
5. 실제 테스트 환경 만들기
현재 사용 중인 프로젝트 경로를 기준으로 설명하겠습니다.
C:\Dev\book_agentic_ai\adk_\
이번 실습은 ADK Web UI를 실행하지 않고, 우선 Python 파일을 직접 실행해서 로깅의 동작을 확인하는 것이 좋습니다.
이유는 간단합니다. Agent나 LLM의 개입 없이 Python 함수와 로깅 모듈 자체의 동작을 독립적으로 확인할 수 있기 때문입니다.
① 테스트 파일 만들기
현재 폴더에 다음 파일을 만듭니다.
weather_logging_test.py
기존에 공유한 코드를 그대로 넣고, 마지막에 테스트용 실행 코드를 추가합니다.
# ============================================================
# 테스트 코드
# ============================================================
if __name__ == "__main__":
print("\n===== 서울 테스트 =====")
result = get_current_weather("서울")
print("서울 날씨 결과:", result)
print("\n===== Seoul 테스트 =====")
result = get_current_weather("Seoul")
print("Seoul 날씨 결과:", result)
print("\n===== 대전 테스트 =====")
result = get_current_weather("대전")
print("대전 날씨 결과:", result)
print("\n===== Daejeon 테스트 =====")
result = get_current_weather("Daejeon")
print("Daejeon 날씨 결과:", result)
print("\n===== 부산 테스트 =====")
result = get_current_weather("부산")
print("부산 날씨 결과:", result)
print("\n===== 테스트 종료 =====")
if __name__ == "__main__":는 무엇일까요?
이 부분은 Python에서 자주 사용하는 실행 제어 구문입니다.
-
해당 파일을 직접 실행하면 __name__이 "__main__"이므로 아래 테스트 코드가 실행됩니다.
-
다른 파일에서 이 파일을 import하면 테스트 코드는 자동으로 실행되지 않습니다.
즉, 이 코드를 통해 함수 정의 부분과 테스트 실행 부분을 분리할 수 있습니다.
② PowerShell에서 실행하기
현재 경로가 다음과 같다고 가정하겠습니다.
(venv) PS C:\Dev\book_agentic_ai\adk_>
다음 명령어를 실행합니다.
python .\weather_logging_test.py
③ 예상 실행 결과
===== 서울 테스트 =====
서울 날씨 결과: {'weather': 'Clear', 'temperature': 22, 'description': '서울은 맑고 쾌청함'}
===== Seoul 테스트 =====
Seoul 날씨 결과: {'weather': 'Clear', 'temperature': 22, 'description': '서울은 맑고 쾌청함'}
===== 대전 테스트 =====
대전 날씨 결과: {'weather': 'Windy', 'temperature': 20, 'description': '대전은 바람이 많이 붑니다'}
===== Daejeon 테스트 =====
Daejeon 날씨 결과: {'weather': 'Windy', 'temperature': 20, 'description': '대전은 바람이 많이 붑니다'}
===== 부산 테스트 =====
부산 날씨 결과: {'error': 'Unsupported Location', 'message': '현재 서울과 대전 지역만 지원합니다.'}
===== 테스트 종료 =====
여기서 주목할 점이 있습니다.
PowerShell 화면에는 print() 결과가 나오지만, 로그 메시지는 화면이 아니라 app.log 파일에 저장됩니다.
filename="app.log"로 설정했기 때문입니다.
6. app.log 파일 열어보기
파일을 실행한 폴더에 app.log가 생성됩니다.
C:\Dev\book_agentic_ai\adk_\
│
├── weather_logging_test.py
└── app.log
PowerShell에서 다음 명령어로 내용을 확인할 수 있습니다.
Get-Content .\app.log
또는 메모장으로 열 수 있습니다.
notepad .\app.log
① 현재 INFO 설정에서 예상되는 로그
시간과 모듈명은 실제 실행 환경에 따라 달라지지만, 로그의 흐름은 다음과 같습니다.
2026-11-08 14:30:00,123 - INFO - __main__ - get_current_weather 호출: location=서울
2026-11-08 14:30:00,124 - INFO - __main__ - validate_location 호출: location=서울
2026-11-08 14:30:00,124 - INFO - __main__ - validate_location: 지원되는 지역으로 판정됨. location=서울, matched_city=서울
2026-11-08 14:30:00,125 - INFO - __main__ - get_current_weather 호출: location=Seoul
2026-11-08 14:30:00,125 - INFO - __main__ - validate_location 호출: location=Seoul
2026-11-08 14:30:00,125 - INFO - __main__ - validate_location: 지원되는 지역으로 판정됨. location=Seoul, matched_city=seoul
2026-11-08 14:30:00,126 - INFO - __main__ - get_current_weather 호출: location=대전
2026-11-08 14:30:00,126 - INFO - __main__ - validate_location 호출: location=대전
2026-11-08 14:30:00,126 - INFO - __main__ - validate_location: 지원되는 지역으로 판정됨. location=대전, matched_city=대전
2026-11-08 14:30:00,127 - INFO - __main__ - get_current_weather 호출: location=Daejeon
2026-11-08 14:30:00,127 - INFO - __main__ - validate_location 호출: location=Daejeon
2026-11-08 14:30:00,127 - INFO - __main__ - validate_location: 지원되는 지역으로 판정됨. location=Daejeon, matched_city=daejeon
2026-11-08 14:30:00,128 - INFO - __main__ - get_current_weather 호출: location=부산
2026-11-08 14:30:00,128 - INFO - __main__ - validate_location 호출: location=부산
2026-11-08 14:30:00,128 - INFO - __main__ - validate_location: 지원되지 않는 지역으로 판정됨. location=부산
2026-11-08 14:30:00,128 - WARNING - __main__ - get_current_weather: 지원되지 않는 위치 요청. location=부산
여기서 중요한 부분은 다음과 같습니다.
-
서울, Seoul, 대전, Daejeon은 모두 정상적으로 지원 지역으로 판정됩니다.
-
부산은 지원되지 않는 지역으로 판정됩니다.
-
부산 요청에서는 WARNING 로그가 추가로 기록됩니다.
-
logger.debug()는 코드에서 호출되지만 INFO 설정이므로 파일에는 나타나지 않습니다.
이것이 로깅 레벨을 설정하는 이유입니다. 정상 실행 기록과 주의가 필요한 상황을 구분해서 관리할 수 있습니다.
7. DEBUG 레벨로 바꾸어 차이를 확인하기
이제 다음 부분을 수정해 보겠습니다.
기존 코드:
logging.basicConfig(
level=logging.INFO,
filename="app.log",
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
)
수정 코드:
logging.basicConfig(
level=logging.DEBUG,
filename="app.log",
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
)
다시 실행합니다.
python .\weather_logging_test.py
이제 app.log에는 다음과 같은 DEBUG 로그도 추가됩니다.
DEBUG - __main__ - get_current_weather: 서울 날씨 반환. location=서울
DEBUG - __main__ - get_current_weather: 대전 날씨 반환. location=대전
즉, DEBUG로 설정하면 함수 내부의 구체적인 처리 경로까지 확인할 수 있습니다.

한 가지 실무적인 주의사항이 있습니다. app.log는 기본적으로 기존 파일에 내용을 이어서 기록합니다. 따라서 재실행하면 이전 로그가 남아 있습니다.
테스트를 매번 깨끗하게 시작하려면 실행 전에 다음 명령으로 기존 로그를 지울 수 있습니다.
Remove-Item .\app.log
그다음 Python 파일을 다시 실행하면 새 로그만 기록됩니다.
또는 설정에 다음 항목을 추가해도 됩니다.
filemode="w"
-
filemode="a": 기존 로그에 추가하는 기본값
-
filemode="w": 기존 로그를 지우고 새로 작성
실제 운영 환경에서는 로그를 덮어쓰지 않는 것이 일반적입니다.
8. 실제 테스트에서 추가로 확인하면 좋은 부분
여기까지는 정상 입력과 미지원 입력을 확인했습니다. 이제 테스트 관점에서 조금 더 깊이 살펴보겠습니다.
① 테스트 결과를 자동으로 검증하기
지금까지는 print()를 사용해 사람이 결과를 눈으로 확인했습니다.
그런데 테스트에서는 예상한 결과가 실제 결과와 같은지 자동으로 검사하는 것이 더 좋습니다.
테스트 코드 마지막 부분에 다음을 추가해 보세요.
# ============================================================
# 5. 결과 검증 테스트
# ============================================================
def test_weather_results():
# 서울 테스트
seoul = get_current_weather("서울")
assert seoul["weather"] == "Clear"
assert seoul["temperature"] == 22
# 대전 테스트
daejeon = get_current_weather("대전")
assert daejeon["weather"] == "Windy"
assert daejeon["temperature"] == 20
# 부산 테스트
busan = get_current_weather("부산")
assert busan["error"] == "Unsupported Location"
print("모든 테스트 통과!")
if __name__ == "__main__":
test_weather_results()
assert는 테스트에서 매우 중요한 명령어입니다.
assert seoul["temperature"] == 22
이 문장은 다음과 같은 뜻입니다.
서울의 기온이 22도라는 조건이 맞는지 확인하라. 맞으면 계속 진행하고, 틀리면 AssertionError를 발생시켜라.
예를 들어 실제 서울 기온을 25도로 변경하면 다음 테스트는 실패합니다.
assert seoul["temperature"] == 22
이렇게 하면 단순히 로그가 잘 기록되는지만 확인하는 것이 아니라, 날씨 조회 함수가 올바른 결과를 반환하는지도 함께 검증할 수 있습니다.
참고로 위의 test_weather_results()를 별도로 실행하려면 기존 if __name__ == "__main__": 아래의 테스트 코드를 이 함수 호출로 교체하면 됩니다.
② 로깅 자체도 테스트할 수 있을까?
네. 예를 들어 다음과 같이 로그 메시지가 기록되었는지를 검사할 수 있습니다.
이런 테스트는 Python의 unittest나 pytest에서 제공하는 로그 캡처 기능을 활용하면 됩니다.
pytest를 사용한다면 다음과 같이 작성할 수 있습니다.
def test_unsupported_location_logging(caplog):
with caplog.at_level(logging.WARNING):
result = get_current_weather("부산")
assert result["error"] == "Unsupported Location"
assert "지원되지 않는 위치 요청" in caplog.text
이 테스트는 세 가지를 확인합니다.
-
부산 요청이 미지원 지역으로 처리되는가?
-
반환 결과에 Unsupported Location이 포함되는가?
-
WARNING 로그에 '지원되지 않는 위치 요청'이라는 메시지가 기록되는가?
이것이 바로 기능 테스트와 로깅 테스트를 함께 수행하는 방식입니다.
9. 현재 코드에서 보완하면 좋은 부분
코드 자체는 로깅 기초 실습으로 적절합니다. 다만 실제 Agent 개발에 적용하기 전에 알아두면 좋은 사항이 세 가지 있습니다.
① 지원 지역 검증이 부분 문자열 방식이다
현재 코드는 다음과 같습니다.
for city in VALID_CITIES:
if city in location.lower():
return True
이 방식은 입력 문자열에 도시명이 포함되어 있는지만 확인합니다.
예를 들어 다음과 같은 입력도 지원 지역으로 잘못 판단할 수 있습니다.
get_current_weather("notseoul")
notseoul 안에 seoul이라는 문자열이 있기 때문입니다.
실제 서비스에서는 입력값을 정규화한 다음 정확히 일치하는지를 확인하는 방식이 더 안전합니다.
② 현재 ERROR 로그는 사실상 실행되지 않는다
다음 코드를 살펴보겠습니다.
logger.error(
"get_current_weather: 처리 불가능한 상태. location=%s",
location,
)
이 코드는 마지막 예외 상황을 기록하기 위해 작성되어 있습니다.
그런데 현재 코드에서는 validate_location()이 True를 반환하는 조건과 실제 서울·대전 처리 조건이 동일합니다.
따라서 정상적으로 문자열이 입력된다는 전제에서는 마지막 logger.error()까지 도달할 수 없습니다.
즉, 현재 테스트에서는 다음 세 가지를 주로 확인하게 됩니다.
-
INFO: 함수 호출 및 지역 판정
-
DEBUG: 정상 도시의 날씨 반환
-
WARNING: 지원하지 않는 지역 요청
ERROR는 별도의 실제 오류 상황을 정의하지 않는 한 확인하기 어렵습니다.
③ basicConfig()는 실행 환경에 따라 주의가 필요하다
이번처럼 독립적인 Python 파일을 실행할 때는 다음 설정이 잘 작동합니다.
logging.basicConfig(...)
하지만 나중에 이 코드를 ADK Agent 프로젝트에 넣으면 이야기가 달라질 수 있습니다.
ADK나 다른 라이브러리가 이미 로깅 핸들러를 설정한 경우에는 basicConfig()가 기존 설정을 변경하지 않을 수 있습니다.
따라서 ADK 프로젝트에서는 로깅 설정을 Agent 코드마다 중복해서 작성하기보다는, 애플리케이션 시작 지점에서 통합 관리하는 편이 좋습니다.
10. 이번 실습의 최종 점검표
다음 순서대로 직접 실행해 보시면 됩니다.
실습 진행 상황
- weather_logging_test.py 파일 생성
11. 이번 실습을 ADK Agent 학습과 연결하면
지금까지 진행한 Weather Agent 학습 과정을 생각하면, 이번 로깅 실습은 상당히 중요한 기반입니다.
예를 들어 앞으로 만들 Manager Agent가 Weather Agent에 날씨 조회를 요청한다고 생각해 보겠습니다.
이때 로그를 잘 설계하면 다음과 같은 실행 흐름을 추적할 수 있습니다.
[INFO] Manager Agent: 사용자 요청 수신
[INFO] Weather Agent: 요청 전달받음
[INFO] get_current_weather: location=Seoul
[INFO] validate_location: 지원되는 지역으로 판정
[DEBUG] 서울 날씨 데이터 반환
[INFO] Weather Agent: 결과 전달 완료
[INFO] Manager Agent: 최종 응답 생성
실제 멀티에이전트 환경에서는 이런 로그가 특히 중요해집니다.
-
어떤 Agent가 실행되었는가?
-
어떤 Tool이 호출되었는가?
-
Tool에 어떤 인자가 전달되었는가?
-
어느 단계에서 오류가 발생했는가?
-
실행 시간이 얼마나 걸렸는가?
이러한 정보를 기록해 두면 나중에 Agent의 실행 오류를 추적하거나 성능을 분석할 수 있습니다.
제가 추천하는 다음 학습 단계는 이번 로깅 코드를 ADK Weather Agent의 Tool에 직접 적용해 보는 것입니다. 단순한 Python 함수의 로그 기록에서 출발해, 실제 ADK Agent의 실행 과정과 Tool 호출 과정을 추적하는 방식으로 발전시키면 좋겠습니다.