helper-dev-utils 0.5.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.
@@ -0,0 +1,63 @@
1
+ # helper_logger 환경변수 설정 예제
2
+ # 이 파일을 .env로 복사하여 사용하세요
3
+
4
+ # =============================================================================
5
+ # 로그 레벨 설정 (우선순위)
6
+ # =============================================================================
7
+
8
+ # 1순위: 전체 로그 레벨 (콘솔과 파일 모두 적용)
9
+ # LOG_LEVEL을 설정하면 LOG_CONSOLE_LEVEL과 LOG_FILE_LEVEL보다 우선합니다
10
+ # 옵션: DEBUG, INFO, WARNING, ERROR, CRITICAL
11
+ LOG_LEVEL=INFO
12
+
13
+ # 2순위: 개별 로그 레벨 (LOG_LEVEL이 설정되지 않은 경우에만 적용)
14
+ # 콘솔 출력 로그 레벨
15
+ LOG_CONSOLE_LEVEL=WARNING
16
+
17
+ # 파일 저장 로그 레벨
18
+ LOG_FILE_LEVEL=DEBUG
19
+
20
+ # =============================================================================
21
+ # 로그 디렉토리 및 파일 설정
22
+ # =============================================================================
23
+
24
+ # 로그 파일이 저장될 디렉토리 경로
25
+ # 기본값: ./logs
26
+ LOG_DIR=./logs
27
+
28
+ # 파일 로깅 활성화 여부
29
+ # 옵션: true, false, 1, 0, yes, no
30
+ # 기본값: false (파일 로깅 비활성화)
31
+ LOG_FILE_ENABLED=false
32
+
33
+ # =============================================================================
34
+ # 사용 예제
35
+ # =============================================================================
36
+ #
37
+ # 예제 1: 모든 로그를 DEBUG 레벨로 설정하고 파일에 저장
38
+ # LOG_LEVEL=DEBUG
39
+ # LOG_FILE_ENABLED=true
40
+ # LOG_DIR=./my_logs
41
+ #
42
+ # 예제 2: 콘솔은 WARNING, 파일은 DEBUG (파일 로깅 활성화)
43
+ # # LOG_LEVEL= (주석 처리)
44
+ # LOG_CONSOLE_LEVEL=WARNING
45
+ # LOG_FILE_LEVEL=DEBUG
46
+ # LOG_FILE_ENABLED=true
47
+ #
48
+ # 예제 3: 환경변수 무시하고 코드에서 직접 설정
49
+ # logger = get_logger("my_app", use_env=False, console_level=logging.INFO)
50
+ #
51
+ # =============================================================================
52
+ # 우선순위 정리
53
+ # =============================================================================
54
+ #
55
+ # 최종 설정 우선순위:
56
+ # 1. 함수 매개변수 (console_level, file_level, file, log_dir 등)
57
+ # 2. 환경변수 (.env 파일)
58
+ # - LOG_LEVEL (전체)
59
+ # - LOG_CONSOLE_LEVEL, LOG_FILE_LEVEL (개별)
60
+ # - LOG_DIR, LOG_FILE_ENABLED
61
+ # 3. 기본값 (console_level=INFO, file_level=DEBUG, file=False, log_dir=./logs)
62
+ #
63
+ # =============================================================================
@@ -0,0 +1,86 @@
1
+ """
2
+ helper-dev-utils
3
+ ============
4
+
5
+ Python 유틸리티 모음 라이브러리
6
+
7
+ 주요 모듈:
8
+ - helper_logger: 로깅 유틸리티 (콘솔/파일 핸들러, 환경변수 기반 설정)
9
+ - helper_pandas: Pandas 확장 기능 (한글 컬럼 설명, 데이터 출력)
10
+ - helper_utils_print: 출력 유틸리티 (디렉토리/JSON/딕셔너리 트리 구조 출력)
11
+ - helper_utils_colab: 경로 관리 유틸리티 (로컬/Colab 환경 경로 자동 탐색)
12
+
13
+ 기본 사용법:
14
+ # Logger
15
+ from helper_dev_utils import get_auto_logger
16
+ logger = get_auto_logger()
17
+ logger.info("Hello World")
18
+
19
+ # Pandas Extension
20
+ from helper_dev_utils import set_pandas_extension
21
+ import pandas as pd
22
+ set_pandas_extension()
23
+ df = pd.DataFrame({'name': ['Alice', 'Bob']})
24
+ df.set_col_description('name', '사용자 이름')
25
+ df.show()
26
+
27
+ # Print Tree
28
+ from helper_dev_utils import print_dir_tree, print_json_tree
29
+ print_dir_tree('/path/to/directory')
30
+ print_json_tree({'key': 'value'})
31
+
32
+ # Colab Path
33
+ from helper_dev_utils import my_driver, my_cache
34
+ driver_path = my_driver() # Google Drive 경로
35
+ cache_path = my_cache() # 캐시 경로
36
+ """
37
+
38
+ __version__ = "0.5.0"
39
+
40
+ # Import main functions from each module
41
+ from .helper_logger import (
42
+ get_logger,
43
+ get_auto_logger,
44
+ sample_logger_env,
45
+ )
46
+
47
+ from .helper_pandas import (
48
+ set_pandas_extension,
49
+ )
50
+
51
+ from .helper_utils_print import (
52
+ print_dir_tree,
53
+ print_json_tree,
54
+ print_dic_tree,
55
+ )
56
+
57
+ from .helper_utils_colab import (
58
+ my_driver,
59
+ my_driver_path,
60
+ my_cache,
61
+ my_cache_path,
62
+ )
63
+
64
+ __all__ = [
65
+ # Logger utilities
66
+ "get_logger",
67
+ "get_auto_logger",
68
+ "sample_logger_env",
69
+
70
+ # Pandas extension
71
+ "set_pandas_extension",
72
+
73
+ # Print utilities
74
+ "print_dir_tree",
75
+ "print_json_tree",
76
+ "print_dic_tree",
77
+
78
+ # Colab/Path utilities
79
+ "my_driver",
80
+ "my_driver_path",
81
+ "my_cache",
82
+ "my_cache_path",
83
+
84
+ # Version
85
+ "__version__",
86
+ ]
@@ -0,0 +1,546 @@
1
+ """
2
+ helper_logger 모듈
3
+
4
+ 간단 설명:
5
+ - 일관된 로깅 설정을 위한 유틸리티를 제공한다.
6
+ - 주요 기능: 단축 레벨 포맷터(ShortLevelFormatter), 호출자 기반 자동 로거(get_auto_logger),
7
+ 콘솔/파일 핸들러 설정(get_logger), 회전 로그(RotatingFileHandler) 지원, 환경변수 샘플 생성(sample_logger_env).
8
+
9
+ 특징:
10
+ - 환경변수(.env) 기반 로그 설정: LOG_LEVEL, LOG_CONSOLE_LEVEL, LOG_FILE_LEVEL, LOG_DIR, LOG_FILE_ENABLED
11
+ - 우선순위: LOG_LEVEL(전체) → LOG_CONSOLE_LEVEL/LOG_FILE_LEVEL(개별) → 함수 매개변수 → 기본값
12
+ - python-dotenv 미설치 시 환경변수 무시하고 기본값 사용
13
+ - 로그 레벨 축약: DEBUG→D, INFO→I, WARNING→W, ERROR→E, CRITICAL→C
14
+ - 시간대: KST(Asia/Seoul) 적용(타임스탬프에 반영)
15
+ - get_logger: console/file 핸들러 구성, max_bytes와 backup_count로 로그 회전 제어
16
+ - get_auto_logger: 호출자 모듈 이름을 자동 추출하여 로거 이름으로 사용(프레임 참조 해제 포함)
17
+ - sample_logger_env: .env.example_logger 샘플 파일 자동 생성
18
+
19
+ 사용 예:
20
+ >>> logger = get_logger("app", console=True, file=True)
21
+ >>> auto = get_auto_logger(console=True)
22
+ >>> env_file = sample_logger_env()
23
+
24
+ 주의:
25
+ - 같은 이름으로 요청하면 동일한 로거 인스턴스를 재사용한다.
26
+ - 로거 레벨은 핸들러 레벨과 조합되어 실제 출력이 결정된다.
27
+
28
+ """
29
+
30
+ import inspect
31
+ import logging
32
+ import logging.handlers
33
+ import os
34
+ import sys
35
+ from datetime import datetime
36
+ from pathlib import Path
37
+ from typing import Optional, Dict, Any, Union
38
+ import pytz
39
+
40
+ # python-dotenv import (선택적 의존성)
41
+ try:
42
+ from dotenv import load_dotenv
43
+ _DOTENV_AVAILABLE = True
44
+ except ImportError:
45
+ _DOTENV_AVAILABLE = False
46
+
47
+ # 전역 변수
48
+ _loggers: Dict[str, logging.Logger] = {}
49
+ try:
50
+ from zoneinfo import ZoneInfo # Python 3.9+
51
+ _kst = ZoneInfo("Asia/Seoul")
52
+ except Exception:
53
+ try:
54
+ import pytz # type: ignore
55
+ _kst = pytz.timezone("Asia/Seoul")
56
+ except Exception:
57
+ # 어떤 타임존 정보도 없으면 시스템 로컬 timezone으로 대체
58
+ _kst = datetime.now().astimezone().tzinfo
59
+
60
+ class ShortLevelFormatter(logging.Formatter):
61
+ """
62
+ 로그 레벨을 단축 표기하는 커스텀 포맷터
63
+
64
+ 로그 레벨을 한 글자로 축약:
65
+ DEBUG→D, INFO→I, WARNING→W, ERROR→E, CRITICAL→C
66
+
67
+ 시간은 KST(Asia/Seoul) 적용
68
+ """
69
+
70
+ LEVEL_MAP: Dict[str, str] = {
71
+ 'DEBUG': 'D',
72
+ 'INFO': 'I',
73
+ 'WARNING': 'W',
74
+ 'ERROR': 'E',
75
+ 'CRITICAL': 'C'
76
+ }
77
+
78
+ def format(self, record: logging.LogRecord) -> str:
79
+ """로그 레벨을 축약하여 포맷"""
80
+ record.levelname = self.LEVEL_MAP.get(record.levelname, record.levelname)
81
+ return super().format(record)
82
+
83
+ def formatTime(
84
+ self,
85
+ record: logging.LogRecord,
86
+ datefmt: Optional[str] = None
87
+ ) -> str:
88
+ """KST 시간으로 변환하여 포맷"""
89
+ ct = datetime.fromtimestamp(record.created, tz=_kst)
90
+ if datefmt:
91
+ return ct.strftime(datefmt)
92
+ return ct.strftime('%Y-%m-%d %H:%M:%S')
93
+
94
+ def _load_env_config(
95
+ env_path: Optional[Path] = None,
96
+ use_env: bool = True
97
+ ) -> Dict[str, Union[int, Path, bool, None]]:
98
+ """
99
+ .env 파일에서 로그 설정을 로드
100
+
101
+ 우선순위:
102
+ 1. LOG_LEVEL (전체 로그 레벨)
103
+ 2. LOG_CONSOLE_LEVEL, LOG_FILE_LEVEL (개별 설정)
104
+ 3. LOG_DIR (로그 디렉토리)
105
+ 4. LOG_FILE_ENABLED (파일 로깅 활성화)
106
+
107
+ Args:
108
+ env_path: .env 파일 경로 (기본: None = 현재 디렉토리)
109
+ use_env: 환경변수 사용 여부 (기본: True)
110
+
111
+ Returns:
112
+ 로그 설정 딕셔너리 {
113
+ 'console_level': Optional[int],
114
+ 'file_level': Optional[int],
115
+ 'log_dir': Optional[Path],
116
+ 'file': Optional[bool]
117
+ }
118
+ """
119
+ config: Dict[str, Union[int, Path, bool, None]] = {
120
+ 'console_level': None,
121
+ 'file_level': None,
122
+ 'log_dir': None,
123
+ 'file': None
124
+ }
125
+
126
+ if not use_env or not _DOTENV_AVAILABLE:
127
+ return config
128
+
129
+ # .env 파일 로드
130
+ if env_path:
131
+ load_dotenv(dotenv_path=env_path, override=False)
132
+ else:
133
+ load_dotenv(override=False)
134
+
135
+ # 로그 레벨 매핑
136
+ level_map = {
137
+ 'DEBUG': logging.DEBUG,
138
+ 'INFO': logging.INFO,
139
+ 'WARNING': logging.WARNING,
140
+ 'ERROR': logging.ERROR,
141
+ 'CRITICAL': logging.CRITICAL
142
+ }
143
+
144
+ # 1순위: LOG_LEVEL (전체)
145
+ log_level_str = os.getenv('LOG_LEVEL', '').upper()
146
+ if log_level_str in level_map:
147
+ config['console_level'] = level_map[log_level_str]
148
+ config['file_level'] = level_map[log_level_str]
149
+
150
+ # 2순위: 개별 설정 (LOG_LEVEL이 없을 때만 적용)
151
+ if config['console_level'] is None:
152
+ console_level_str = os.getenv('LOG_CONSOLE_LEVEL', '').upper()
153
+ if console_level_str in level_map:
154
+ config['console_level'] = level_map[console_level_str]
155
+
156
+ if config['file_level'] is None:
157
+ file_level_str = os.getenv('LOG_FILE_LEVEL', '').upper()
158
+ if file_level_str in level_map:
159
+ config['file_level'] = level_map[file_level_str]
160
+
161
+ # LOG_DIR
162
+ log_dir_str = os.getenv('LOG_DIR', '').strip()
163
+ if log_dir_str:
164
+ config['log_dir'] = Path(log_dir_str)
165
+
166
+ # LOG_FILE_ENABLED
167
+ log_file_enabled_str = os.getenv('LOG_FILE_ENABLED', '').strip().lower()
168
+ if log_file_enabled_str in ('true', '1', 'yes'):
169
+ config['file'] = True
170
+ elif log_file_enabled_str in ('false', '0', 'no'):
171
+ config['file'] = False
172
+
173
+ return config
174
+
175
+ def get_logger(
176
+ name: str,
177
+ console: bool = True,
178
+ console_level: Optional[int] = None,
179
+ file: Optional[bool] = None,
180
+ file_level: Optional[int] = None,
181
+ log_level: Optional[int] = None,
182
+ log_dir: Optional[Path] = None,
183
+ max_bytes: int = 10 * 1024 * 1024, # 10MB
184
+ backup_count: int = 5,
185
+ use_env: bool = True,
186
+ env_path: Optional[Path] = None
187
+ ) -> logging.Logger:
188
+ """
189
+ 로거 인스턴스 생성 및 반환
190
+
191
+ 환경변수 우선순위 (.env 파일):
192
+ 1. LOG_LEVEL: 전체 로그 레벨 설정
193
+ 2. LOG_CONSOLE_LEVEL, LOG_FILE_LEVEL: 개별 설정
194
+ 3. LOG_DIR: 로그 디렉토리
195
+ 4. LOG_FILE_ENABLED: 파일 로깅 활성화 (true/false)
196
+
197
+ 최종 우선순위: 함수 매개변수 → 환경변수 → 기본값
198
+
199
+ Args:
200
+ name: 로거 이름
201
+ console: 콘솔 출력 여부 (기본: True)
202
+ console_level: 콘솔 로그 레벨 (기본: None → 환경변수 → INFO)
203
+ file: 파일 저장 여부 (기본: None → 환경변수 → False)
204
+ file_level: 파일 로그 레벨 (기본: None → 환경변수 → DEBUG)
205
+ log_level: 전체 로그 레벨 (console_level, file_level 우선)
206
+ log_dir: 로그 파일 저장 디렉토리 (기본: None → 환경변수 → ./logs)
207
+ max_bytes: 로그 파일 최대 크기 (기본: 10MB)
208
+ backup_count: 백업 파일 개수 (기본: 5)
209
+ use_env: 환경변수 사용 여부 (기본: True)
210
+ env_path: .env 파일 경로 (기본: None = 현재 디렉토리)
211
+
212
+ Returns:
213
+ 설정된 로거 인스턴스
214
+
215
+ Examples:
216
+ >>> logger = get_logger("my_app") # 환경변수 적용
217
+ >>> logger = get_logger("my_app", use_env=False) # 환경변수 무시
218
+ >>> logger = get_logger("my_app", console_level=logging.WARNING) # 명시적 설정
219
+ >>> logger = get_logger("my_app", env_path=Path(".env.local")) # 커스텀 .env
220
+ """
221
+ # 중복 생성 방지
222
+ if name in _loggers:
223
+ return _loggers[name]
224
+
225
+ # 환경변수 로드
226
+ env_config = _load_env_config(env_path=env_path, use_env=use_env)
227
+
228
+ # 우선순위: 함수 매개변수 → 환경변수 → 기본값
229
+ final_console_level: int
230
+ final_file_level: int
231
+ final_file: bool
232
+ final_log_dir: Optional[Path]
233
+
234
+ if log_level is not None:
235
+ final_console_level = log_level
236
+ final_file_level = log_level
237
+ else:
238
+ if console_level is None:
239
+ env_console = env_config['console_level']
240
+ final_console_level = env_console if isinstance(env_console, int) else logging.INFO
241
+ else:
242
+ final_console_level = console_level
243
+
244
+ if file_level is None:
245
+ env_file = env_config['file_level']
246
+ final_file_level = env_file if isinstance(env_file, int) else logging.INFO
247
+ else:
248
+ final_file_level = file_level
249
+
250
+ if file is None:
251
+ env_file_flag = env_config['file']
252
+ final_file = env_file_flag if isinstance(env_file_flag, bool) else False
253
+ else:
254
+ final_file = file
255
+
256
+ if log_dir is None:
257
+ env_log_dir = env_config['log_dir']
258
+ final_log_dir = env_log_dir if isinstance(env_log_dir, Path) else None
259
+ else:
260
+ final_log_dir = log_dir
261
+
262
+ logger = logging.getLogger(name)
263
+ # 로거 레벨은 가장 낮은 레벨로 설정 (핸들러에서 필터링)
264
+ logger.setLevel(min(final_console_level, final_file_level) if final_file else final_console_level)
265
+ logger.propagate = False
266
+
267
+ # 포맷터 생성
268
+ formatter = ShortLevelFormatter(
269
+ # fmt='%(asctime)s %(levelname)s [%(name)s] %(filename)s:%(lineno)d - %(funcName)s() - %(message)s',
270
+ fmt='%(asctime)s %(levelname)s [%(name)s:%(lineno)d] - %(message)s',
271
+ datefmt='%Y-%m-%d %H:%M:%S'
272
+ )
273
+
274
+ # 콘솔 핸들러
275
+ if console:
276
+ console_handler = logging.StreamHandler(sys.stdout)
277
+ console_handler.setLevel(final_console_level)
278
+ console_handler.setFormatter(formatter)
279
+ logger.addHandler(console_handler)
280
+
281
+ # 파일 핸들러
282
+ if final_file:
283
+ if final_log_dir is None:
284
+ final_log_dir = Path('./logs')
285
+ final_log_dir.mkdir(parents=True, exist_ok=True)
286
+
287
+ log_file = final_log_dir / f"{name}.log"
288
+ file_handler = logging.handlers.RotatingFileHandler(
289
+ log_file,
290
+ maxBytes=max_bytes,
291
+ backupCount=backup_count,
292
+ encoding='utf-8'
293
+ )
294
+ file_handler.setLevel(final_file_level)
295
+ file_handler.setFormatter(formatter)
296
+ logger.addHandler(file_handler)
297
+
298
+ _loggers[name] = logger
299
+ return logger
300
+
301
+
302
+ def get_auto_logger(**kwargs) -> logging.Logger:
303
+ """
304
+ 호출자 모듈 이름을 기반으로 자동 로거 생성 및 반환.
305
+
306
+ 동작:
307
+ - 현재 프레임의 한 단계 위(f_back)에서 호출자 정보를 얻어 호출자 모듈의
308
+ __file__ 값을 사용하여 로거 이름(Path(...).stem)을 결정합니다.
309
+ - 호출자 프레임 또는 __file__이 없는 경우 sys.argv[0] 또는 '__main__'을
310
+ 대체값으로 사용합니다.
311
+ - 프레임 참조는 finally 블록에서 삭제하여 참조 순환(memory leak)을 방지합니다.
312
+ - 전달된 모든 kwargs는 내부의 get_logger에 그대로 전달됩니다.
313
+ - 기본적으로 환경변수(.env) 설정을 자동 적용합니다 (use_env=True).
314
+
315
+ Args:
316
+ **kwargs: get_logger로 전달할 옵션들
317
+ - console, file, console_level, file_level, log_level, log_dir
318
+ - use_env: 환경변수 사용 여부 (기본: True)
319
+ - env_path: .env 파일 경로 (기본: None)
320
+
321
+ Returns:
322
+ logging.Logger: 생성되거나 재사용된 로거 인스턴스
323
+
324
+ Examples:
325
+ >>> logger = get_auto_logger() # 환경변수 자동 적용
326
+ >>> logger = get_auto_logger(use_env=False) # 환경변수 무시
327
+ >>> logger = get_auto_logger(console_level=logging.DEBUG)
328
+ >>> logger = get_auto_logger(file=True, env_path=Path(".env.local"))
329
+ """
330
+ frame = inspect.currentframe()
331
+ try:
332
+ caller = frame.f_back if frame is not None else None
333
+
334
+ # 호출자 전역에서 '__file__'을 시도하여 호출자 모듈 경로를 획득
335
+ caller_file = None
336
+ if caller is not None:
337
+ caller_file = caller.f_globals.get('__file__')
338
+
339
+ # __file__이 없거나 호출자 정보가 없을 때의 안전한 대체값
340
+ if not caller_file:
341
+ caller_file = sys.argv[0] if len(sys.argv) > 0 and sys.argv[0] else '__main__'
342
+
343
+ name = Path(caller_file).stem
344
+ finally:
345
+ # 프레임 참조 해제(참조 순환 방지)
346
+ try:
347
+ del frame
348
+ except Exception:
349
+ pass
350
+
351
+ return get_logger(name, **kwargs)
352
+
353
+ def sample_logger_env(output_dir: Optional[Path] = None) -> Path:
354
+ """
355
+ .env.example_logger 샘플 파일 생성
356
+
357
+ 현재 폴더(또는 지정된 폴더)에 helper_logger의 환경변수 설정 예제 파일을 생성합니다.
358
+ 파일명: .env.example_logger
359
+
360
+ Args:
361
+ output_dir: 파일을 생성할 디렉토리 (기본: None = 현재 디렉토리)
362
+
363
+ Returns:
364
+ 생성된 파일의 경로 (Path 객체)
365
+
366
+ Examples:
367
+ >>> env_file = sample_logger_env()
368
+ >>> print(env_file)
369
+ Path('d:/path/to/.env.example_logger')
370
+
371
+ >>> env_file = sample_logger_env(output_dir=Path("./config"))
372
+ """
373
+ if output_dir is None:
374
+ output_dir = Path.cwd()
375
+ else:
376
+ output_dir = Path(output_dir)
377
+
378
+ # 디렉토리 생성
379
+ output_dir.mkdir(parents=True, exist_ok=True)
380
+
381
+ # .env.example_logger 파일 내용
382
+ env_content = """# helper_logger 환경변수 설정 예제
383
+ # 이 파일을 .env로 복사하여 사용하세요
384
+
385
+ # =============================================================================
386
+ # 로그 레벨 설정 (우선순위)
387
+ # =============================================================================
388
+
389
+ # 1순위: 전체 로그 레벨 (콘솔과 파일 모두 적용)
390
+ # LOG_LEVEL을 설정하면 LOG_CONSOLE_LEVEL과 LOG_FILE_LEVEL보다 우선합니다
391
+ # 옵션: DEBUG, INFO, WARNING, ERROR, CRITICAL
392
+ LOG_LEVEL=INFO
393
+
394
+ # 2순위: 개별 로그 레벨 (LOG_LEVEL이 설정되지 않은 경우에만 적용)
395
+ # 콘솔 출력 로그 레벨
396
+ LOG_CONSOLE_LEVEL=WARNING
397
+
398
+ # 파일 저장 로그 레벨
399
+ LOG_FILE_LEVEL=DEBUG
400
+
401
+ # =============================================================================
402
+ # 로그 디렉토리 및 파일 설정
403
+ # =============================================================================
404
+
405
+ # 로그 파일이 저장될 디렉토리 경로
406
+ # 기본값: ./logs
407
+ LOG_DIR=./logs
408
+
409
+ # 파일 로깅 활성화 여부
410
+ # 옵션: true, false, 1, 0, yes, no
411
+ # 기본값: false (파일 로깅 비활성화)
412
+ LOG_FILE_ENABLED=false
413
+
414
+ # =============================================================================
415
+ # 사용 예제
416
+ # =============================================================================
417
+ #
418
+ # 예제 1: 모든 로그를 DEBUG 레벨로 설정하고 파일에 저장
419
+ # LOG_LEVEL=DEBUG
420
+ # LOG_FILE_ENABLED=true
421
+ # LOG_DIR=./my_logs
422
+ #
423
+ # 예제 2: 콘솔은 WARNING, 파일은 DEBUG (파일 로깅 활성화)
424
+ # # LOG_LEVEL= (주석 처리)
425
+ # LOG_CONSOLE_LEVEL=WARNING
426
+ # LOG_FILE_LEVEL=DEBUG
427
+ # LOG_FILE_ENABLED=true
428
+ #
429
+ # 예제 3: 환경변수 무시하고 코드에서 직접 설정
430
+ # logger = get_logger("my_app", use_env=False, console_level=logging.INFO)
431
+ #
432
+ # =============================================================================
433
+ # 우선순위 정리
434
+ # =============================================================================
435
+ #
436
+ # 최종 설정 우선순위:
437
+ # 1. 함수 매개변수 (console_level, file_level, file, log_dir 등)
438
+ # 2. 환경변수 (.env 파일)
439
+ # - LOG_LEVEL (전체)
440
+ # - LOG_CONSOLE_LEVEL, LOG_FILE_LEVEL (개별)
441
+ # - LOG_DIR, LOG_FILE_ENABLED
442
+ # 3. 기본값 (console_level=INFO, file_level=DEBUG, file=False, log_dir=./logs)
443
+ #
444
+ # =============================================================================
445
+ """
446
+
447
+ # 파일 생성
448
+ output_file = output_dir / ".env.example_logger"
449
+ output_file.write_text(env_content, encoding='utf-8')
450
+
451
+ return output_file
452
+
453
+ # 모듈 레벨 기본 로거
454
+ logger = get_auto_logger()
455
+
456
+ if __name__ == "__main__":
457
+ """로거 기능 테스트"""
458
+
459
+ print("=" * 60)
460
+ print("로거 테스트 시작")
461
+ print("=" * 60)
462
+
463
+ # 테스트 1: 콘솔만 (기본)
464
+ print("\n[테스트 1] 콘솔 전용 로거 (INFO 레벨)")
465
+ test_logger1 = get_logger("test_console")
466
+ test_logger1.debug("디버그 메시지 - 출력 안 됨")
467
+ test_logger1.info("인포 메시지")
468
+ test_logger1.warning("경고 메시지")
469
+ test_logger1.error("에러 메시지")
470
+ test_logger1.critical("크리티컬 메시지")
471
+
472
+ # 테스트 2: 콘솔 + 파일
473
+ print("\n[테스트 2] 콘솔(INFO) + 파일(DEBUG)")
474
+ test_logger2 = get_logger("test_both", console=True, file=True)
475
+ test_logger2.debug("디버그 - 파일에만 기록")
476
+ test_logger2.info("인포 - 콘솔 + 파일")
477
+ test_logger2.warning("경고 - 콘솔 + 파일")
478
+ print(f"로그 파일: ./logs/test_both.log")
479
+
480
+ # 테스트 3: 로그 레벨 다르게 설정
481
+ print("\n[테스트 3] 콘솔(WARNING) vs 파일(DEBUG)")
482
+ test_logger3 = get_logger(
483
+ "test_levels",
484
+ console=True,
485
+ console_level=logging.WARNING,
486
+ file=True,
487
+ file_level=logging.DEBUG
488
+ )
489
+ test_logger3.debug("디버그 - 파일에만")
490
+ test_logger3.info("인포 - 파일에만")
491
+ test_logger3.warning("경고 - 콘솔 + 파일")
492
+ test_logger3.error("에러 - 콘솔 + 파일")
493
+ print(f"로그 파일: ./logs/test_levels.log")
494
+
495
+ # 테스트 4: 커스텀 경로
496
+ print("\n[테스트 4] 커스텀 로그 디렉토리")
497
+ custom_dir = Path("./test_logs")
498
+ test_logger4 = get_logger(
499
+ "test_custom",
500
+ console=False,
501
+ file=True,
502
+ log_dir=custom_dir
503
+ )
504
+ test_logger4.info("커스텀 경로에 저장")
505
+ print(f"로그 파일: {custom_dir}/test_custom.log")
506
+
507
+ # 테스트 5: 중복 생성 방지 확인
508
+ print("\n[테스트 5] 중복 생성 방지")
509
+ logger_a = get_logger("duplicate_test")
510
+ logger_b = get_logger("duplicate_test")
511
+ print(f"같은 인스턴스: {logger_a is logger_b}")
512
+
513
+ # 테스트 6: 환경변수 사용 (use_env=True)
514
+ print("\n[테스트 6] 환경변수 설정 테스트")
515
+ print("환경변수 로드 가능 여부:", "가능" if _DOTENV_AVAILABLE else "불가능 (python-dotenv 미설치)")
516
+ test_logger6 = get_logger("test_env", use_env=True)
517
+ test_logger6.debug("환경변수 기반 DEBUG")
518
+ test_logger6.info("환경변수 기반 INFO")
519
+ test_logger6.warning("환경변수 기반 WARNING")
520
+ print("환경변수(.env) 설정이 적용됩니다 (LOG_LEVEL, CONSOLE_LOG_LEVEL 등)")
521
+
522
+ # 테스트 7: 환경변수 무시 (use_env=False)
523
+ print("\n[테스트 7] 환경변수 무시")
524
+ test_logger7 = get_logger("test_no_env", use_env=False, console_level=logging.WARNING)
525
+ test_logger7.info("INFO - 출력 안 됨")
526
+ test_logger7.warning("WARNING - 출력됨")
527
+ print("환경변수를 무시하고 함수 매개변수만 사용")
528
+
529
+ # 테스트 8: get_auto_logger 환경변수
530
+ print("\n[테스트 8] get_auto_logger 환경변수 자동 적용")
531
+ auto_logger = get_auto_logger()
532
+ auto_logger.info("자동 로거 - 환경변수 적용")
533
+ print(f"자동 로거 이름: {auto_logger.name}")
534
+
535
+ # 테스트 9: sample_logger_env 함수
536
+ print("\n[테스트 9] sample_logger_env() - 샘플 파일 생성")
537
+ try:
538
+ env_file = sample_logger_env()
539
+ print(f"✓ 생성 성공: {env_file}")
540
+ print(f" 파일 크기: {env_file.stat().st_size} bytes")
541
+ except Exception as e:
542
+ print(f"✗ 생성 실패: {e}")
543
+
544
+ print("\n" + "=" * 60)
545
+ print("테스트 완료")
546
+ print("=" * 60)