pyjallib 0.1.16__tar.gz → 0.1.17__tar.gz

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.
Files changed (102) hide show
  1. {pyjallib-0.1.16 → pyjallib-0.1.17}/PKG-INFO +1 -1
  2. pyjallib-0.1.17/PRD//353/241/234/352/271/205 /353/252/250/353/223/210 /354/240/234/354/236/221.md" +618 -0
  3. pyjallib-0.1.17/logs/20250702_max_fbx_export.log +4 -0
  4. pyjallib-0.1.17/logs/20250702_project_specific.log +3 -0
  5. pyjallib-0.1.17/logs/pyjallib.log +0 -0
  6. {pyjallib-0.1.16 → pyjallib-0.1.17}/pyproject.toml +21 -21
  7. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/__init__.py +4 -2
  8. pyjallib-0.1.17/src/pyjallib/logger.py +201 -0
  9. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/bip.py +0 -21
  10. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/naming.py +4 -1
  11. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/perforce.py +183 -264
  12. pyjallib-0.1.17/src/pyjallib/progressEvent.py +75 -0
  13. pyjallib-0.1.17/src/pyjallib/py.typed +0 -0
  14. pyjallib-0.1.17/src/pyjallib/ue5/ConfigFiles/UE5NamingConfig.json +487 -0
  15. pyjallib-0.1.17/src/pyjallib/ue5/__init__.py +170 -0
  16. pyjallib-0.1.17/src/pyjallib/ue5/disableInterchangeFrameWork.py +82 -0
  17. pyjallib-0.1.17/src/pyjallib/ue5/inUnreal/__init__.py +95 -0
  18. pyjallib-0.1.17/src/pyjallib/ue5/inUnreal/animationImporter.py +114 -0
  19. pyjallib-0.1.17/src/pyjallib/ue5/inUnreal/baseImporter.py +184 -0
  20. pyjallib-0.1.17/src/pyjallib/ue5/inUnreal/importerSettings.py +179 -0
  21. pyjallib-0.1.17/src/pyjallib/ue5/inUnreal/skeletalMeshImporter.py +112 -0
  22. pyjallib-0.1.17/src/pyjallib/ue5/inUnreal/skeletonImporter.py +105 -0
  23. pyjallib-0.1.17/src/pyjallib/ue5/logger.py +200 -0
  24. pyjallib-0.1.17/src/pyjallib/ue5/templateProcessor.py +254 -0
  25. pyjallib-0.1.17/src/pyjallib/ue5/templates/__init__.py +106 -0
  26. pyjallib-0.1.17/src/pyjallib/ue5/templates/animImportTemplate.py +22 -0
  27. pyjallib-0.1.17/src/pyjallib/ue5/templates/skeletalMeshImportTemplate.py +22 -0
  28. pyjallib-0.1.17/src/pyjallib/ue5/templates/skeletonImportTemplate.py +21 -0
  29. pyjallib-0.1.17/tests/animImportTest.py +31 -0
  30. pyjallib-0.1.17/tests/perforce_logging_example.py +224 -0
  31. pyjallib-0.1.17/tests/skelImportTest.py +19 -0
  32. pyjallib-0.1.17/tests/template_processor_test.py +90 -0
  33. pyjallib-0.1.17/tests/ueImportTest.py +60 -0
  34. {pyjallib-0.1.16 → pyjallib-0.1.17}/uv.lock +1 -1
  35. {pyjallib-0.1.16 → pyjallib-0.1.17}/.gitignore +0 -0
  36. {pyjallib-0.1.16 → pyjallib-0.1.17}/.python-version +0 -0
  37. {pyjallib-0.1.16 → pyjallib-0.1.17}/PRD/BIPPY_/353/260/224/354/235/264/355/214/250/353/223/234_/354/203/235/354/204/261_/353/266/204/354/204/235.md" +0 -0
  38. {pyjallib-0.1.16 → pyjallib-0.1.17}/PRD/PyJalLib_Mocap_Module_PRD.md +0 -0
  39. {pyjallib-0.1.16 → pyjallib-0.1.17}/README.md +0 -0
  40. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/index.html +0 -0
  41. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/pyjallib/max.html +0 -0
  42. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/pyjallib/namePart.html +0 -0
  43. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/pyjallib/nameToPath.html +0 -0
  44. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/pyjallib/naming.html +0 -0
  45. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/pyjallib/namingConfig.html +0 -0
  46. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/pyjallib/p4module.html +0 -0
  47. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/pyjallib/perforce.html +0 -0
  48. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/pyjallib/reloadModules.html +0 -0
  49. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/pyjallib.html +0 -0
  50. {pyjallib-0.1.16 → pyjallib-0.1.17}/docs/search.js +0 -0
  51. {pyjallib-0.1.16 → pyjallib-0.1.17}/generate_docs.ps1 +0 -0
  52. /pyjallib-0.1.16/src/pyjallib/py.typed → /pyjallib-0.1.17/logs/20250702_pyjallib.log +0 -0
  53. {pyjallib-0.1.16 → pyjallib-0.1.17}/ref/BIPPY.ms +0 -0
  54. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/ConfigFiles/namingConfig.json +0 -0
  55. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/ConfigFiles/3DSMaxNamingConfig.json +0 -0
  56. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/ConfigFiles/Default_3DSMaxNamingConfig.json +0 -0
  57. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/__init__.py +0 -0
  58. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/align.py +0 -0
  59. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/anim.py +0 -0
  60. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/autoClavicle.py +0 -0
  61. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/bone.py +0 -0
  62. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/boneChain.py +0 -0
  63. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/constraint.py +0 -0
  64. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/fbxHandler.py +0 -0
  65. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/groinBone.py +0 -0
  66. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/header.py +0 -0
  67. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/helper.py +0 -0
  68. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/hip.py +0 -0
  69. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/kneeBone.py +0 -0
  70. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/layer.py +0 -0
  71. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/link.py +0 -0
  72. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/macro/jal_macro_align.py +0 -0
  73. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/macro/jal_macro_bone.py +0 -0
  74. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/macro/jal_macro_constraint.py +0 -0
  75. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/macro/jal_macro_helper.py +0 -0
  76. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/macro/jal_macro_link.py +0 -0
  77. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/macro/jal_macro_select.py +0 -0
  78. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/mirror.py +0 -0
  79. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/mocap.py +0 -0
  80. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/morph.py +0 -0
  81. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/name.py +0 -0
  82. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/progress.py +0 -0
  83. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/rootMotion.py +0 -0
  84. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/select.py +0 -0
  85. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/skeleton.py +0 -0
  86. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/skin.py +0 -0
  87. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/toolManager.py +0 -0
  88. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/twistBone.py +0 -0
  89. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/ui/Container.py +0 -0
  90. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/max/volumeBone.py +0 -0
  91. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/namePart.py +0 -0
  92. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/nameToPath.py +0 -0
  93. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/namingConfig.py +0 -0
  94. {pyjallib-0.1.16 → pyjallib-0.1.17}/src/pyjallib/reloadModules.py +0 -0
  95. {pyjallib-0.1.16 → pyjallib-0.1.17}/test_debug.log +0 -0
  96. {pyjallib-0.1.16 → pyjallib-0.1.17}/tests/animNodeTest.py +0 -0
  97. {pyjallib-0.1.16 → pyjallib-0.1.17}/tests/autoclavicleTest.py +0 -0
  98. {pyjallib-0.1.16 → pyjallib-0.1.17}/tests/fbxExportTest.py +0 -0
  99. {pyjallib-0.1.16 → pyjallib-0.1.17}/tests/globalVarTest.py +0 -0
  100. {pyjallib-0.1.16 → pyjallib-0.1.17}/tests/moduleImportTest.py +0 -0
  101. {pyjallib-0.1.16 → pyjallib-0.1.17}/tests/p4Test.py +0 -0
  102. {pyjallib-0.1.16 → pyjallib-0.1.17}/tests/volumePreserveBoneTest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyjallib
3
- Version: 0.1.16
3
+ Version: 0.1.17
4
4
  Summary: A utility library for 3D game character development pipelines.
5
5
  Author-email: Dongseok Kim <jalnagakds@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -0,0 +1,618 @@
1
+ # PyJalLib 중앙 집중식 로깅 모듈 PRD
2
+ ## Product Requirements Document
3
+
4
+ **프로젝트명**: PyJalLib 중앙 집중식 로깅 모듈 개발
5
+ **버전**: 1.0
6
+ **작성일**: 2025년 1월 18일
7
+ **작성자**: Development Team
8
+
9
+ ---
10
+
11
+ ## 1. 프로젝트 개요
12
+
13
+ ### 1.1 목적
14
+ PyJalLib의 모든 모듈에서 사용할 수 있는 중앙 집중식 로깅 시스템을 구축하여 일관된 로깅 방식을 제공하고, 로그 관리의 효율성을 향상시킨다.
15
+
16
+ ### 1.2 배경
17
+ - 현재 각 모듈(UE5, Perforce 등)이 독립적인 로깅 시스템을 사용
18
+ - 로그 형식, 저장 위치, 레벨 관리가 모듈마다 상이함
19
+ - 통합된 로그 분석 및 디버깅의 어려움
20
+ - 중복된 로깅 설정 코드로 인한 유지보수성 저하
21
+
22
+ ### 1.3 범위
23
+ - 단일 로깅 클래스 구현 (`Logger`)
24
+ - 모듈별 로거 인스턴스 제공
25
+ - 통합된 로그 형식 및 저장 정책
26
+ - 런타임 로그 레벨 동적 변경
27
+ - 기존 모듈들의 단순한 로깅 코드 교체
28
+
29
+ ---
30
+
31
+ ## 2. 요구사항 분석
32
+
33
+ ### 2.1 기능적 요구사항 (Functional Requirements)
34
+
35
+ #### 2.1.1 핵심 기능 (Core Features)
36
+ | 기능 ID | 기능명 | 설명 | 우선순위 |
37
+ |---------|--------|------|----------|
38
+ | F001 | Logger 클래스 | 간단한 로거 인스턴스 제공 | High |
39
+ | F002 | 파일 로깅 | 로그 파일에 메시지 기록 | High |
40
+ | F003 | 콘솔 로깅 | 콘솔에 메시지 출력 | High |
41
+ | F004 | UE5 로깅 | UE5 환경에서 UE5 콘솔에 출력 | Medium |
42
+ | F005 | 세션 구분 기능 | 동적 세션 변경 및 구분선 출력 | Medium |
43
+
44
+ #### 2.1.2 추가 기능 (Additional Features)
45
+ | 기능 ID | 기능명 | 설명 | 우선순위 |
46
+ |---------|--------|------|----------|
47
+ | F006 | 기본 로그 레벨 설정 | 생성 시 로그 레벨 설정 | Low |
48
+
49
+ ### 2.2 비기능적 요구사항 (Non-Functional Requirements)
50
+
51
+ #### 2.2.1 성능 요구사항
52
+ - 로그 메시지 처리 지연시간: 1ms 이하
53
+ - 메모리 사용량: 기본 상태에서 10MB 이하
54
+ - 로그 파일 I/O 오버헤드: 기존 대비 5% 이내
55
+
56
+ #### 2.2.2 호환성 요구사항
57
+ - Python 3.7 이상 지원
58
+ - 기존 UE5, Perforce 모듈과 완전 호환
59
+ - 3DS Max, Unreal Engine 환경에서 동작
60
+
61
+ #### 2.2.3 확장성 요구사항
62
+ - 새로운 로그 핸들러 쉽게 추가 가능
63
+ - 플러그인 방식으로 확장 기능 지원
64
+ - 다국어 로그 메시지 지원 준비
65
+
66
+ ---
67
+
68
+ ## 3. 현재 상태 분석
69
+
70
+ ### 3.1 기존 로깅 시스템 현황
71
+
72
+ #### 3.1.1 UE5 모듈 (`src/pyjallib/ue5/logger.py`)
73
+ **구현 상태**: ✅ 완료
74
+ **특징**:
75
+ - UE5 전용 핸들러 구현 (`UE5LogHandler`)
76
+ - 파일 + UE5 콘솔 이중 출력
77
+ - 날짜별 로그 파일 생성
78
+ - 동적 로그 레벨 설정 지원
79
+
80
+ **코드 구조**:
81
+ ```python
82
+ class UE5LogHandler(logging.Handler)
83
+ def _setup_ue5_logging()
84
+ def set_log_level(inLevel: str)
85
+ def set_ue5_log_level(inLevel: str)
86
+ ```
87
+
88
+ #### 3.1.2 Perforce 모듈 (`src/pyjallib/perforce.py`)
89
+ **구현 상태**: ✅ 완료
90
+ **특징**:
91
+ - 기본 파일 핸들러 사용
92
+ - 디버그 모드 생성자 지원
93
+ - 단일 로그 파일 (`Perforce.log`)
94
+
95
+ **코드 구조**:
96
+ ```python
97
+ logger = logging.getLogger(__name__)
98
+ file_handler = logging.FileHandler(str(log_path), encoding='utf-8')
99
+ ```
100
+
101
+ ### 3.2 문제점 분석
102
+
103
+ #### 3.2.1 코드 중복 문제
104
+ - 로그 경로 설정: `Path.home() / "Documents" / "PyJalLib" / "logs"`
105
+ - 포맷터 설정: 각 모듈마다 유사한 형식 정의
106
+ - 핸들러 관리: 중복된 핸들러 추가/제거 로직
107
+
108
+ #### 3.2.2 일관성 부족
109
+ - 로그 파일명 규칙: `ue5_module_20250118.log` vs `Perforce.log`
110
+ - 인코딩 설정: UE5는 명시적 UTF-8, Perforce도 UTF-8이지만 중복 설정
111
+ - 로그 레벨 기본값: UE5는 DEBUG, Perforce는 ERROR
112
+
113
+ #### 3.2.3 확장성 제한
114
+ - 새 모듈 추가 시 로깅 설정 재구현 필요
115
+ - 통합된 로그 레벨 관리 불가능
116
+ - 런타임 설정 변경의 어려움
117
+
118
+ ---
119
+
120
+ ## 4. 기술적 사양
121
+
122
+ ### 4.1 아키텍처 설계
123
+
124
+ #### 4.1.1 모듈 구조
125
+ ```
126
+ src/pyjallib/
127
+ └── logger.py # 메인 로깅 클래스
128
+ ```
129
+
130
+ #### 4.1.2 클래스 구조
131
+ ```mermaid
132
+ classDiagram
133
+ class Logger {
134
+ -_logger: logging.Logger
135
+ -_logPath: Path
136
+ -_enableConsole: bool
137
+ -_enableUE5: bool
138
+ -_sessionName: str
139
+ +__init__(inLogPath: str, inEnableConsole: bool, inEnableUE5: bool)
140
+ +debug(inMessage: str)
141
+ +info(inMessage: str)
142
+ +warning(inMessage: str)
143
+ +error(inMessage: str)
144
+ +critical(inMessage: str)
145
+ +set_session(inSessionName: str)
146
+ +end_session()
147
+ -_setup_handlers()
148
+ -_get_formatter() logging.Formatter
149
+ -_create_ue5_handler() logging.Handler
150
+ -_log_separator(inMessage: str)
151
+ }
152
+ ```
153
+
154
+ ### 4.2 핵심 클래스 설계
155
+
156
+ #### 4.2.1 Logger 클래스
157
+ ```python
158
+ class Logger:
159
+ """PyJalLib 간단한 로깅 클래스"""
160
+
161
+ def __init__(self, inLogPath: str = None, inEnableConsole: bool = True, inEnableUE5: bool = False):
162
+ """로거 인스턴스 초기화
163
+
164
+ Args:
165
+ inLogPath (str, optional): 로그 파일 저장 경로.
166
+ None인 경우 기본 경로 사용 (Documents/PyJalLib/logs)
167
+ inEnableConsole (bool): 콘솔 출력 활성화 여부 (기본값: True)
168
+ inEnableUE5 (bool): UE5 출력 활성화 여부 (기본값: False)
169
+ """
170
+ # 기본 로그 경로 설정
171
+ if inLogPath is None:
172
+ documents_path = Path.home() / "Documents"
173
+ self._logPath = documents_path / "PyJalLib" / "logs"
174
+ else:
175
+ self._logPath = Path(inLogPath)
176
+
177
+ # 로그 디렉토리 생성
178
+ self._logPath.mkdir(parents=True, exist_ok=True)
179
+
180
+ # 출력 옵션 설정
181
+ self._enableConsole = inEnableConsole
182
+ self._enableUE5 = inEnableUE5
183
+ self._sessionName = None # 초기에는 세션 없음
184
+
185
+ # 로거 생성 및 설정
186
+ self._logger = logging.getLogger(f"pyjallib_{id(self)}")
187
+ self._logger.setLevel(logging.DEBUG)
188
+ self._logger.handlers.clear() # 기존 핸들러 제거
189
+ self._setup_handlers()
190
+
191
+ def debug(self, inMessage: str) -> None:
192
+ """디버그 레벨 로그 메시지"""
193
+ self._logger.debug(inMessage)
194
+
195
+ def info(self, inMessage: str) -> None:
196
+ """정보 레벨 로그 메시지"""
197
+ self._logger.info(inMessage)
198
+
199
+ def warning(self, inMessage: str) -> None:
200
+ """경고 레벨 로그 메시지"""
201
+ self._logger.warning(inMessage)
202
+
203
+ def error(self, inMessage: str) -> None:
204
+ """에러 레벨 로그 메시지"""
205
+ self._logger.error(inMessage)
206
+
207
+ def critical(self, inMessage: str) -> None:
208
+ """치명적 에러 레벨 로그 메시지"""
209
+ self._logger.critical(inMessage)
210
+
211
+ def set_session(self, inSessionName: str) -> None:
212
+ """새로운 로깅 세션 설정 및 시작
213
+
214
+ Args:
215
+ inSessionName (str): 세션 구분용 이름
216
+ """
217
+ # 기존 세션이 있다면 종료
218
+ if self._sessionName is not None:
219
+ self.end_session()
220
+
221
+ # 새 세션 시작
222
+ self._sessionName = inSessionName
223
+ separator_msg = f"===== {self._sessionName} 로깅 시작 ====="
224
+ self._log_separator(separator_msg)
225
+
226
+ def end_session(self) -> None:
227
+ """현재 로깅 세션 종료 구분선 출력"""
228
+ if self._sessionName is not None:
229
+ separator_msg = f"===== {self._sessionName} 로깅 끝 ====="
230
+ self._log_separator(separator_msg)
231
+ self._sessionName = None
232
+
233
+ def _log_separator(self, inMessage: str) -> None:
234
+ """구분선 메시지를 모든 핸들러에 직접 출력"""
235
+ # 구분선은 INFO 레벨로 출력하되, 특별한 포맷 사용
236
+ separator_record = logging.LogRecord(
237
+ name=self._logger.name,
238
+ level=logging.INFO,
239
+ pathname="",
240
+ lineno=0,
241
+ msg=inMessage,
242
+ args=(),
243
+ exc_info=None
244
+ )
245
+
246
+ # 각 핸들러에 직접 전송 (포맷터 우회)
247
+ for handler in self._logger.handlers:
248
+ if handler.isEnabledFor(logging.INFO):
249
+ # 구분선만 특별한 포맷으로 출력
250
+ formatted_msg = f"{inMessage}"
251
+ handler.stream.write(formatted_msg + "\n") if hasattr(handler, 'stream') else None
252
+ handler.flush() if hasattr(handler, 'flush') else None
253
+
254
+ def _setup_handlers(self) -> None:
255
+ """로거에 핸들러 설정"""
256
+ # 파일 핸들러 (항상 활성화)
257
+ log_file = self._logPath / "pyjallib.log"
258
+ file_handler = logging.FileHandler(log_file, encoding='utf-8')
259
+ file_handler.setFormatter(self._get_formatter())
260
+ self._logger.addHandler(file_handler)
261
+
262
+ # 콘솔 핸들러 (선택사항)
263
+ if self._enableConsole:
264
+ console_handler = logging.StreamHandler()
265
+ console_handler.setFormatter(self._get_formatter())
266
+ self._logger.addHandler(console_handler)
267
+
268
+ # UE5 핸들러 (선택사항)
269
+ if self._enableUE5:
270
+ ue5_handler = self._create_ue5_handler()
271
+ if ue5_handler:
272
+ self._logger.addHandler(ue5_handler)
273
+
274
+ def _get_formatter(self) -> logging.Formatter:
275
+ """표준 포맷터 반환"""
276
+ return logging.Formatter(
277
+ '%(asctime)s - %(levelname)s - %(message)s',
278
+ datefmt='%Y-%m-%d %H:%M:%S'
279
+ )
280
+
281
+ def _create_ue5_handler(self) -> logging.Handler:
282
+ """UE5 핸들러 생성 (기존 UE5LogHandler 코드 활용)"""
283
+ # 기존 UE5LogHandler 코드를 여기에 통합
284
+ try:
285
+ import unreal
286
+
287
+ class UE5LogHandler(logging.Handler):
288
+ def emit(self, record):
289
+ try:
290
+ message = self.format(record)
291
+ if record.levelno >= logging.ERROR:
292
+ unreal.log_error(message)
293
+ elif record.levelno >= logging.WARNING:
294
+ unreal.log_warning(message)
295
+ else:
296
+ unreal.log(message)
297
+ except Exception:
298
+ pass
299
+
300
+ return UE5LogHandler()
301
+ except ImportError:
302
+ return None
303
+ ```
304
+
305
+ ### 4.3 사용법
306
+
307
+ #### 4.3.1 기본 사용법
308
+ ```python
309
+ from pyjallib.logger import Logger
310
+
311
+ # 기본 로거 생성 (파일 + 콘솔 출력)
312
+ logger = Logger()
313
+
314
+ # 사용자 지정 경로로 생성
315
+ logger = Logger("C:/MyProject/logs")
316
+
317
+ # 콘솔 출력 비활성화
318
+ logger = Logger(inEnableConsole=False)
319
+
320
+ # UE5 환경에서 UE5 출력 활성화
321
+ logger = Logger(inEnableUE5=True)
322
+
323
+ # 세션 시작
324
+ logger.set_session("UE5 Import")
325
+
326
+ # 로그 메시지 출력
327
+ logger.debug("디버그 메시지")
328
+ logger.info("정보 메시지")
329
+ logger.warning("경고 메시지")
330
+ logger.error("에러 메시지")
331
+ logger.critical("치명적 에러 메시지")
332
+
333
+ # 작업 완료 시 세션 종료
334
+ logger.end_session()
335
+
336
+ # 다른 작업 시작
337
+ logger.set_session("Animation Export")
338
+ logger.info("애니메이션 익스포트 시작")
339
+ logger.end_session()
340
+ ```
341
+
342
+ #### 4.3.1.1 로그 출력 예시
343
+ ```
344
+ ===== UE5 Import 로깅 시작 =====
345
+ 2025-01-18 14:30:15 - INFO - FBX 파일 로드 시작
346
+ 2025-01-18 14:30:16 - INFO - 스켈레톤 생성 완료
347
+ 2025-01-18 14:30:17 - WARNING - 일부 본이 누락됨
348
+ 2025-01-18 14:30:18 - INFO - 임포트 완료
349
+ ===== UE5 Import 로깅 끝 =====
350
+ ===== UE5 Mesh Import 로깅 시작 =====
351
+ 2025-01-18 14:31:00 - INFO - 메시 임포트 시작
352
+ 2025-01-18 14:31:01 - INFO - 메시 임포트 완료
353
+ ===== UE5 Mesh Import 로깅 끝 =====
354
+ ```
355
+
356
+ #### 4.3.2 각 모듈에서의 사용법
357
+ ```python
358
+ # UE5 모듈에서
359
+ from pyjallib.logger import Logger
360
+
361
+ # UE5 출력 활성화된 로거 생성
362
+ logger = Logger(inEnableUE5=True)
363
+ logger.set_session("UE5 Animation Import")
364
+ logger.info("애니메이션 임포트 시작")
365
+ logger.info("FBX 파일 분석 중...")
366
+ logger.error("UE5에서 오류 발생")
367
+ logger.end_session() # 작업 완료 시 호출
368
+
369
+ # 같은 로거로 다른 작업 수행
370
+ logger.set_session("UE5 Mesh Import")
371
+ logger.info("메시 임포트 시작")
372
+ logger.end_session()
373
+
374
+ # Perforce 모듈에서
375
+ from pyjallib.logger import Logger
376
+
377
+ class Perforce:
378
+ def __init__(self, debugMode: bool = False):
379
+ # 디버그 모드에 따라 콘솔 출력 제어
380
+ self.logger = Logger(inEnableConsole=debugMode)
381
+
382
+ def sync_project(self, projectName: str):
383
+ self.logger.set_session(f"Perforce Sync - {projectName}")
384
+ self.logger.debug("Perforce 연결 시도")
385
+ self.logger.info("Perforce 연결 성공")
386
+ self.logger.info("동기화 완료")
387
+ self.logger.end_session()
388
+
389
+ def submit_files(self, description: str):
390
+ self.logger.set_session("Perforce Submit")
391
+ self.logger.info(f"파일 제출: {description}")
392
+ self.logger.end_session()
393
+
394
+ # Max 모듈에서 - 파일만 출력
395
+ from pyjallib.logger import Logger
396
+
397
+ logger = Logger(inEnableConsole=False)
398
+ logger.set_session("3DS Max Export")
399
+ logger.info("Max 스크립트 실행")
400
+ logger.info("FBX 익스포트 완료")
401
+ logger.end_session()
402
+
403
+ # 같은 로거로 다른 익스포트 작업
404
+ logger.set_session("3DS Max Animation Export")
405
+ logger.info("애니메이션 익스포트 시작")
406
+ logger.end_session()
407
+ ```
408
+
409
+
410
+
411
+ ---
412
+
413
+ ## 5. 구현 계획
414
+
415
+ ### 5.1 개발 단계별 계획
416
+
417
+ #### Phase 1: Logger 클래스 구현 (2일)
418
+ **목표**: 핵심 로깅 클래스 구현
419
+ **주요 작업**:
420
+ - [ ] `Logger` 클래스 구현 (인스턴스 기반)
421
+ - [ ] 기본 파일 핸들러 구현
422
+ - [ ] 콘솔 핸들러 구현
423
+ - [ ] 기본 포맷터 구현
424
+
425
+ **완료 기준**:
426
+ - 기본적인 로거 생성 및 관리 기능 동작
427
+ - 파일 및 콘솔 로깅 기능 구현
428
+ - 단위 테스트 통과
429
+
430
+ #### Phase 2: UE5 핸들러 통합 (1일)
431
+ **목표**: 기존 UE5 핸들러 통합
432
+ **주요 작업**:
433
+ - [ ] 기존 `UE5LogHandler` 코드 통합
434
+ - [ ] UE5 출력 활성화/비활성화 기능
435
+ - [ ] UE5 환경 감지 및 fallback 처리
436
+
437
+ **완료 기준**:
438
+ - UE5 환경에서 정상 동작
439
+ - UE5가 없는 환경에서도 정상 동작
440
+ - 기존 UE5 로깅 기능과 동일한 동작
441
+
442
+ #### Phase 3: 테스트 및 검증 (1일)
443
+ **목표**: 다양한 환경에서 Logger 클래스 테스트
444
+ **주요 작업**:
445
+ - [ ] 단위 테스트 작성 및 실행
446
+ - [ ] 파일 로깅 기능 검증
447
+ - [ ] 콘솔 로깅 기능 검증
448
+ - [ ] UE5 환경에서 테스트 (가능한 경우)
449
+
450
+ **완료 기준**:
451
+ - 모든 기본 기능이 정상 동작
452
+ - 세션 구분 기능 정상 동작
453
+ - 다양한 환경에서 안정성 확인
454
+
455
+ ---
456
+
457
+ ## 6. 테스트 전략
458
+
459
+ ### 6.1 단위 테스트
460
+
461
+ #### 6.1.1 Logger 테스트
462
+ ```python
463
+ def test_instance_creation():
464
+ """인스턴스 생성 및 독립성 검증"""
465
+
466
+ def test_logger_creation():
467
+ """로거 생성 및 관리 검증"""
468
+
469
+ def test_level_setting():
470
+ """로그 레벨 설정 검증"""
471
+ ```
472
+
473
+ #### 6.1.2 핸들러 테스트
474
+ ```python
475
+ def test_file_handler():
476
+ """파일 핸들러 기본 동작 검증"""
477
+
478
+ def test_ue5_handler_fallback():
479
+ """UE5 핸들러 fallback 동작 검증"""
480
+
481
+ def test_console_handler():
482
+ """콘솔 핸들러 출력 검증"""
483
+ ```
484
+
485
+ ### 6.2 통합 테스트
486
+
487
+ #### 6.2.1 환경별 테스트
488
+ - Python 독립 환경에서 테스트
489
+ - 3DS Max 환경에서 테스트 (가능한 경우)
490
+ - UE5 환경에서 테스트 (가능한 경우)
491
+
492
+ ### 6.3 사용자 수락 테스트
493
+
494
+ #### 6.3.1 기본 기능 검증
495
+ - [ ] 로그 파일이 예상 위치에 생성
496
+ - [ ] 모든 로그 레벨이 정상 출력
497
+ - [ ] 콘솔 출력 활성화/비활성화 동작
498
+ - [ ] UE5 환경에서 UE5 콘솔 출력 (UE5 환경에서)
499
+
500
+ #### 6.3.2 세션 기능 검증
501
+ - [ ] 세션 시작 구분선 자동 출력
502
+ - [ ] 세션 종료 구분선 수동 출력
503
+ - [ ] 사용자 정의 세션명 정상 표시
504
+
505
+ ---
506
+
507
+ ## 7. 위험 요소 및 대응 방안
508
+
509
+ ### 7.1 기술적 위험
510
+
511
+ #### 7.1.1 성능 저하 위험
512
+ **위험**: 중앙 집중식 로깅으로 인한 성능 오버헤드
513
+ **대응 방안**:
514
+ - 비동기 로깅 처리 고려
515
+ - 로그 레벨 기반 조기 필터링
516
+ - 핸들러별 스레드 풀 적용
517
+
518
+ #### 7.1.2 호환성 문제
519
+ **위험**: 기존 모듈과의 호환성 이슈
520
+ **대응 방안**:
521
+ - 점진적 마이그레이션 전략
522
+ - 기존 API 래퍼 함수 제공
523
+ - 충분한 테스트 커버리지 확보
524
+
525
+ ### 7.2 운영 위험
526
+
527
+ #### 7.2.1 로그 파일 크기 증가
528
+ **위험**: 통합 로깅으로 인한 로그 파일 크기 급증
529
+ **대응 방안**:
530
+ - 자동 로테이션 및 압축 기능
531
+ - 로그 레벨별 분리 저장
532
+ - 오래된 로그 자동 삭제 정책
533
+
534
+ #### 7.2.2 설정 복잡성
535
+ **위험**: 설정 파일의 복잡성 증가
536
+ **대응 방안**:
537
+ - 직관적인 기본 설정 제공
538
+ - 설정 검증 및 에러 메시지 개선
539
+ - 단계별 설정 가이드 문서 제공
540
+
541
+ ---
542
+
543
+ ## 8. 성공 측정 지표
544
+
545
+ ### 8.1 기능적 지표
546
+ - [ ] Logger 클래스 기본 기능 동작 (debug, info, warning, error, critical)
547
+ - [ ] 파일 로깅 기능 동작
548
+ - [ ] 콘솔 로깅 기능 동작
549
+ - [ ] UE5 로깅 기능 동작 (UE5 환경에서)
550
+ - [ ] 세션 시작/종료 구분선 기능 동작
551
+
552
+ ### 8.2 성능 지표
553
+ - [ ] 기존 대비 성능 저하 없음
554
+ - [ ] 로깅 관련 메모리 사용량 증가 최소화
555
+ - [ ] 파일 I/O 성능 유지
556
+
557
+ ### 8.3 품질 지표
558
+ - [ ] 단위 테스트 커버리지 80% 이상
559
+ - [ ] 통합 테스트 100% 통과
560
+ - [ ] 기존 기능 완전 호환성 유지
561
+
562
+ ---
563
+
564
+ ## 9. 배포 계획
565
+
566
+ ### 9.1 배포 단계
567
+
568
+ #### 9.1.1 개발 완료 (4일 후)
569
+ - Logger 클래스 구현 완료
570
+ - 기본 테스트 통과
571
+ - 문서화 완료
572
+
573
+ #### 9.1.2 검증 단계 (필요시)
574
+ - 다양한 환경에서 추가 테스트
575
+ - 사용자 피드백 수집
576
+ - 버그 수정 및 개선
577
+
578
+ #### 9.1.3 배포 준비
579
+ - 최종 코드 검토
580
+ - 사용 가이드 작성
581
+ - 예제 코드 준비
582
+
583
+ ---
584
+
585
+ ## 10. 추후 확장 계획
586
+
587
+ ### 10.1 단기 확장 (필요시)
588
+ - 날짜별 로그 파일 자동 로테이션
589
+ - 로그 레벨별 파일 분리
590
+ - 간단한 로그 검색 기능
591
+
592
+ ### 10.2 중기 확장 (필요시)
593
+ - 로그 압축 및 아카이브 기능
594
+ - 외부 로그 시스템 연동 (선택사항)
595
+ - 성능 모니터링 추가
596
+
597
+ ---
598
+
599
+ ## 11. 참고 문서
600
+
601
+ ### 11.1 기존 구현 참고
602
+ - `src/pyjallib/ue5/logger.py` - UE5LogHandler 구현 참고
603
+
604
+ ### 11.2 외부 참고 자료
605
+ - [Python Logging Documentation](https://docs.python.org/3/library/logging.html)
606
+ - [Python Logging Cookbook](https://docs.python.org/3/howto/logging-cookbook.html)
607
+ - [Clean Architecture Logging Patterns](https://blog.cleancoder.com/)
608
+
609
+ ### 11.3 설계 원칙
610
+ - **SOLID 원칙**: 단일 책임, 개방-폐쇄, 의존성 역전 적용
611
+ - **Clean Architecture**: 도메인 로직과 인프라 관심사 분리
612
+ - **Factory Pattern**: 객체 생성 로직 캡슐화
613
+ - **Singleton Pattern**: 전역 상태 관리
614
+
615
+ ---
616
+
617
+ **승인자**: ________________
618
+ **승인일**: ________________
@@ -0,0 +1,4 @@
1
+ ===== FBX Export 로깅 시작 =====
2
+ 2025-07-02 10:58:34 - INFO - Max 스크립트 시작
3
+ 2025-07-02 10:58:34 - INFO - FBX 익스포트 완료
4
+ ===== FBX Export 로깅 끝 =====
@@ -0,0 +1,3 @@
1
+ ===== 경로와 파일명 지정 테스트 로깅 시작 =====
2
+ 2025-07-02 10:59:42 - INFO - 지정된 경로에 사용자 파일명으로 로그 생성
3
+ ===== 경로와 파일명 지정 테스트 로깅 끝 =====
File without changes
@@ -1,21 +1,21 @@
1
- [project]
2
- name = "pyjallib"
3
- version = "0.1.16"
4
- description = "A utility library for 3D game character development pipelines."
5
- readme = "README.md"
6
- authors = [
7
- { name = "Dongseok Kim", email = "jalnagakds@gmail.com" }
8
- ]
9
- requires-python = ">=3.10"
10
- dependencies = [
11
- "p4python>=2024.2.2682690",
12
- ]
13
-
14
- [build-system]
15
- requires = ["hatchling"]
16
- build-backend = "hatchling.build"
17
-
18
- [tool.pdoc]
19
- modules = ["pyjallib"]
20
- output_directory = "docs"
21
- docformat = "markdown"
1
+ [project]
2
+ name = "pyjallib"
3
+ version = "0.1.17"
4
+ description = "A utility library for 3D game character development pipelines."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Dongseok Kim", email = "jalnagakds@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "p4python>=2024.2.2682690",
12
+ ]
13
+
14
+ [build-system]
15
+ requires = ["hatchling"]
16
+ build-backend = "hatchling.build"
17
+
18
+ [tool.pdoc]
19
+ modules = ["pyjallib"]
20
+ output_directory = "docs"
21
+ docformat = "markdown"
@@ -6,7 +6,7 @@ pyjallib Package
6
6
  Python library for game character development pipeline.
7
7
  """
8
8
 
9
- __version__ = '0.1.16'
9
+ __version__ = '0.1.17'
10
10
 
11
11
  # reload_modules 함수를 패키지 레벨에서 사용 가능하게 함
12
12
  from pyjallib.namePart import NamePart, NamePartType
@@ -14,4 +14,6 @@ from pyjallib.naming import Naming
14
14
  from pyjallib.namingConfig import NamingConfig
15
15
  from pyjallib.nameToPath import NameToPath
16
16
  from pyjallib.perforce import Perforce
17
- from pyjallib.reloadModules import reload_modules
17
+ from pyjallib.reloadModules import reload_modules
18
+ from pyjallib.logger import Logger
19
+ from pyjallib.progressEvent import ProgressEvent