log-collector-async 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- log_collector/__init__.py +13 -0
- log_collector/async_client.py +651 -0
- log_collector_async-1.1.0.dist-info/METADATA +804 -0
- log_collector_async-1.1.0.dist-info/RECORD +10 -0
- log_collector_async-1.1.0.dist-info/WHEEL +5 -0
- log_collector_async-1.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +1 -0
- tests/test_async_client.py +292 -0
- tests/test_integration.py +372 -0
- tests/test_performance.py +143 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
성능 테스트: 처리량, 지연시간, 메모리 사용량 벤치마크
|
|
3
|
+
실행 전 요구사항:
|
|
4
|
+
1. 로그 서버 실행 (localhost:8000)
|
|
5
|
+
2. 로컬 환경 권장 (네트워크 지연 최소화)
|
|
6
|
+
"""
|
|
7
|
+
import pytest
|
|
8
|
+
import time
|
|
9
|
+
import timeit
|
|
10
|
+
import tracemalloc
|
|
11
|
+
from log_collector import AsyncLogClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def check_server_running():
|
|
16
|
+
"""로그 서버 실행 여부 확인"""
|
|
17
|
+
import requests
|
|
18
|
+
try:
|
|
19
|
+
response = requests.get("http://localhost:8000", timeout=1)
|
|
20
|
+
if response.status_code != 200:
|
|
21
|
+
pytest.skip("로그 서버가 실행되지 않았습니다")
|
|
22
|
+
except requests.exceptions.RequestException:
|
|
23
|
+
pytest.skip("로그 서버가 실행되지 않았습니다")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_throughput(check_server_running):
|
|
27
|
+
"""처리량 테스트: 10,000건/초 목표"""
|
|
28
|
+
client = AsyncLogClient("http://localhost:8000", batch_size=1000)
|
|
29
|
+
|
|
30
|
+
count = 10000
|
|
31
|
+
start = time.time()
|
|
32
|
+
|
|
33
|
+
for i in range(count):
|
|
34
|
+
client.log("INFO", f"perf test {i}", test_id="throughput_test")
|
|
35
|
+
|
|
36
|
+
elapsed = time.time() - start
|
|
37
|
+
throughput = count / elapsed
|
|
38
|
+
|
|
39
|
+
print(f"\n처리량 테스트 결과:")
|
|
40
|
+
print(f" 총 로그: {count}개")
|
|
41
|
+
print(f" 소요 시간: {elapsed:.3f}초")
|
|
42
|
+
print(f" 처리량: {throughput:.0f} logs/sec")
|
|
43
|
+
print(f" 로그당 시간: {elapsed/count*1000:.3f}ms")
|
|
44
|
+
|
|
45
|
+
# 목표: 10,000 logs/sec = 0.1ms/log
|
|
46
|
+
# 너무 엄격한 제한은 CI 환경에서 실패할 수 있으므로 완화된 기준 사용
|
|
47
|
+
assert throughput > 5000, f"처리량 {throughput:.0f} logs/sec이 목표 5,000 logs/sec 미달"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_latency(check_server_running):
|
|
51
|
+
"""지연시간 테스트: < 0.1ms 목표"""
|
|
52
|
+
client = AsyncLogClient("http://localhost:8000")
|
|
53
|
+
|
|
54
|
+
# 1000번 반복 평균
|
|
55
|
+
time_per_call = timeit.timeit(
|
|
56
|
+
lambda: client.log("INFO", "latency test", test_id="latency_test"),
|
|
57
|
+
number=1000
|
|
58
|
+
) / 1000
|
|
59
|
+
|
|
60
|
+
print(f"\n지연시간 테스트 결과:")
|
|
61
|
+
print(f" 호출당 지연시간: {time_per_call*1000:.3f}ms")
|
|
62
|
+
print(f" 목표: < 0.1ms")
|
|
63
|
+
|
|
64
|
+
# 실제 환경에서는 0.1ms보다 약간 클 수 있으므로 완화된 기준
|
|
65
|
+
assert time_per_call < 0.001, f"지연시간 {time_per_call*1000:.3f}ms이 목표 1ms 초과"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_memory_usage(check_server_running):
|
|
69
|
+
"""메모리 사용량 테스트: < 10MB 목표"""
|
|
70
|
+
tracemalloc.start()
|
|
71
|
+
|
|
72
|
+
client = AsyncLogClient("http://localhost:8000", batch_size=1000)
|
|
73
|
+
|
|
74
|
+
# 10,000건 로깅
|
|
75
|
+
for i in range(10000):
|
|
76
|
+
client.log("INFO", f"memory test {i}", test_id="memory_test")
|
|
77
|
+
|
|
78
|
+
current, peak = tracemalloc.get_traced_memory()
|
|
79
|
+
tracemalloc.stop()
|
|
80
|
+
|
|
81
|
+
peak_mb = peak / 1024 / 1024
|
|
82
|
+
current_mb = current / 1024 / 1024
|
|
83
|
+
|
|
84
|
+
print(f"\n메모리 사용량 테스트 결과:")
|
|
85
|
+
print(f" 현재 메모리: {current_mb:.2f}MB")
|
|
86
|
+
print(f" 피크 메모리: {peak_mb:.2f}MB")
|
|
87
|
+
print(f" 목표: < 10MB")
|
|
88
|
+
|
|
89
|
+
assert peak_mb < 20, f"피크 메모리 {peak_mb:.2f}MB이 목표 20MB 초과"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_concurrent_logging(check_server_running):
|
|
93
|
+
"""동시 로깅 성능 테스트"""
|
|
94
|
+
client = AsyncLogClient("http://localhost:8000", batch_size=100)
|
|
95
|
+
|
|
96
|
+
count = 1000
|
|
97
|
+
start = time.time()
|
|
98
|
+
|
|
99
|
+
# 빠르게 연속으로 로그 생성 (비동기 큐잉 테스트)
|
|
100
|
+
for i in range(count):
|
|
101
|
+
client.log("INFO", f"concurrent test {i}", iteration=i, test_id="concurrent_test")
|
|
102
|
+
|
|
103
|
+
elapsed = time.time() - start
|
|
104
|
+
throughput = count / elapsed
|
|
105
|
+
|
|
106
|
+
print(f"\n동시 로깅 성능 테스트:")
|
|
107
|
+
print(f" 총 로그: {count}개")
|
|
108
|
+
print(f" 소요 시간: {elapsed:.3f}초")
|
|
109
|
+
print(f" 처리량: {throughput:.0f} logs/sec")
|
|
110
|
+
|
|
111
|
+
# 비동기 큐잉이므로 매우 빠르게 처리되어야 함
|
|
112
|
+
assert throughput > 1000, f"처리량 {throughput:.0f} logs/sec이 목표 1,000 logs/sec 미달"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_batch_efficiency(check_server_running):
|
|
116
|
+
"""배치 효율성 테스트: 배치 크기별 성능 비교"""
|
|
117
|
+
batch_sizes = [10, 100, 1000]
|
|
118
|
+
results = {}
|
|
119
|
+
|
|
120
|
+
for batch_size in batch_sizes:
|
|
121
|
+
client = AsyncLogClient("http://localhost:8000", batch_size=batch_size)
|
|
122
|
+
|
|
123
|
+
count = 1000
|
|
124
|
+
start = time.time()
|
|
125
|
+
|
|
126
|
+
for i in range(count):
|
|
127
|
+
client.log("INFO", f"batch efficiency test {i}", batch_size=batch_size)
|
|
128
|
+
|
|
129
|
+
# 전송 완료 대기
|
|
130
|
+
client.flush()
|
|
131
|
+
time.sleep(0.5)
|
|
132
|
+
|
|
133
|
+
elapsed = time.time() - start
|
|
134
|
+
throughput = count / elapsed
|
|
135
|
+
results[batch_size] = throughput
|
|
136
|
+
|
|
137
|
+
print(f"\n배치 크기별 성능 비교:")
|
|
138
|
+
for batch_size, throughput in results.items():
|
|
139
|
+
print(f" 배치 크기 {batch_size}: {throughput:.0f} logs/sec")
|
|
140
|
+
|
|
141
|
+
# 모든 배치 크기에서 최소 성능 기준 충족
|
|
142
|
+
for batch_size, throughput in results.items():
|
|
143
|
+
assert throughput > 500, f"배치 크기 {batch_size}에서 성능 미달: {throughput:.0f} logs/sec"
|