pyjallib 0.1.16__tar.gz → 0.1.19__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.
- {pyjallib-0.1.16 → pyjallib-0.1.19}/PKG-INFO +1 -1
- pyjallib-0.1.19/PRD//353/241/234/352/271/205 /353/252/250/353/223/210 /354/240/234/354/236/221.md" +618 -0
- pyjallib-0.1.19/logs/20250702_max_fbx_export.log +4 -0
- pyjallib-0.1.19/logs/20250702_project_specific.log +3 -0
- pyjallib-0.1.19/logs/pyjallib.log +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/pyproject.toml +2 -2
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/__init__.py +4 -1
- pyjallib-0.1.19/src/pyjallib/exceptions.py +75 -0
- pyjallib-0.1.19/src/pyjallib/logger.py +288 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/__init__.py +8 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/autoClavicle.py +17 -5
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/bip.py +0 -21
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/bone.py +21 -1
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/boneChain.py +2 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/constraint.py +27 -2
- pyjallib-0.1.19/src/pyjallib/max/elbow.py +105 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/groinBone.py +2 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/header.py +121 -113
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/hip.py +2 -0
- pyjallib-0.1.19/src/pyjallib/max/inguinal.py +117 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/kneeBone.py +2 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/macro/jal_macro_bone.py +221 -8
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/macro/jal_macro_constraint.py +30 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/mirror.py +20 -13
- pyjallib-0.1.19/src/pyjallib/max/shoulder.py +173 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/twistBone.py +22 -19
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/volumeBone.py +12 -1
- pyjallib-0.1.19/src/pyjallib/max/wrist.py +113 -0
- pyjallib-0.1.19/src/pyjallib/nameToPath.py +130 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/naming.py +4 -1
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/perforce.py +196 -347
- pyjallib-0.1.19/src/pyjallib/progressEvent.py +75 -0
- pyjallib-0.1.19/src/pyjallib/py.typed +0 -0
- pyjallib-0.1.19/src/pyjallib/ue5/ConfigFiles/UE5NamingConfig.json +487 -0
- pyjallib-0.1.19/src/pyjallib/ue5/__init__.py +170 -0
- pyjallib-0.1.19/src/pyjallib/ue5/disableInterchangeFrameWork.py +82 -0
- pyjallib-0.1.19/src/pyjallib/ue5/inUnreal/__init__.py +95 -0
- pyjallib-0.1.19/src/pyjallib/ue5/inUnreal/animationImporter.py +206 -0
- pyjallib-0.1.19/src/pyjallib/ue5/inUnreal/baseImporter.py +187 -0
- pyjallib-0.1.19/src/pyjallib/ue5/inUnreal/importerSettings.py +179 -0
- pyjallib-0.1.19/src/pyjallib/ue5/inUnreal/skeletalMeshImporter.py +112 -0
- pyjallib-0.1.19/src/pyjallib/ue5/inUnreal/skeletonImporter.py +105 -0
- pyjallib-0.1.19/src/pyjallib/ue5/logger.py +241 -0
- pyjallib-0.1.19/src/pyjallib/ue5/templateProcessor.py +287 -0
- pyjallib-0.1.19/src/pyjallib/ue5/templates/__init__.py +109 -0
- pyjallib-0.1.19/src/pyjallib/ue5/templates/animImportTemplate.py +22 -0
- pyjallib-0.1.19/src/pyjallib/ue5/templates/batchAnimImportTemplate.py +21 -0
- pyjallib-0.1.19/src/pyjallib/ue5/templates/skeletalMeshImportTemplate.py +22 -0
- pyjallib-0.1.19/src/pyjallib/ue5/templates/skeletonImportTemplate.py +21 -0
- pyjallib-0.1.19/tests/animImportTest.py +31 -0
- pyjallib-0.1.19/tests/example_perforce_usage.py +110 -0
- pyjallib-0.1.19/tests/perforce_exception_test.py +167 -0
- pyjallib-0.1.19/tests/perforce_logging_example.py +194 -0
- pyjallib-0.1.19/tests/skelImportTest.py +19 -0
- pyjallib-0.1.19/tests/template_processor_test.py +90 -0
- pyjallib-0.1.19/tests/ueImportTest.py +60 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/uv.lock +1 -1
- pyjallib-0.1.16/src/pyjallib/nameToPath.py +0 -113
- {pyjallib-0.1.16 → pyjallib-0.1.19}/.gitignore +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/.python-version +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/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
- {pyjallib-0.1.16 → pyjallib-0.1.19}/PRD/PyJalLib_Mocap_Module_PRD.md +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/README.md +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/index.html +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/pyjallib/max.html +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/pyjallib/namePart.html +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/pyjallib/nameToPath.html +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/pyjallib/naming.html +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/pyjallib/namingConfig.html +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/pyjallib/p4module.html +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/pyjallib/perforce.html +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/pyjallib/reloadModules.html +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/pyjallib.html +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/docs/search.js +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/generate_docs.ps1 +0 -0
- /pyjallib-0.1.16/src/pyjallib/py.typed → /pyjallib-0.1.19/logs/20250702_pyjallib.log +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/ref/BIPPY.ms +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/ConfigFiles/namingConfig.json +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/ConfigFiles/3DSMaxNamingConfig.json +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/ConfigFiles/Default_3DSMaxNamingConfig.json +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/align.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/anim.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/fbxHandler.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/helper.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/layer.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/link.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/macro/jal_macro_align.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/macro/jal_macro_helper.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/macro/jal_macro_link.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/macro/jal_macro_select.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/mocap.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/morph.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/name.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/progress.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/rootMotion.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/select.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/skeleton.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/skin.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/toolManager.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/max/ui/Container.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/namePart.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/namingConfig.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/src/pyjallib/reloadModules.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/test_debug.log +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/tests/animNodeTest.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/tests/autoclavicleTest.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/tests/fbxExportTest.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/tests/globalVarTest.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/tests/moduleImportTest.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/tests/p4Test.py +0 -0
- {pyjallib-0.1.16 → pyjallib-0.1.19}/tests/volumePreserveBoneTest.py +0 -0
pyjallib-0.1.19/PRD//353/241/234/352/271/205 /353/252/250/353/223/210 /354/240/234/354/236/221.md"
ADDED
@@ -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
|
+
**승인일**: ________________
|
File without changes
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "pyjallib"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.19"
|
4
4
|
description = "A utility library for 3D game character development pipelines."
|
5
5
|
readme = "README.md"
|
6
6
|
authors = [
|
@@ -18,4 +18,4 @@ build-backend = "hatchling.build"
|
|
18
18
|
[tool.pdoc]
|
19
19
|
modules = ["pyjallib"]
|
20
20
|
output_directory = "docs"
|
21
|
-
docformat = "markdown"
|
21
|
+
docformat = "markdown"
|
@@ -6,9 +6,12 @@ pyjallib Package
|
|
6
6
|
Python library for game character development pipeline.
|
7
7
|
"""
|
8
8
|
|
9
|
-
__version__ = '0.1.
|
9
|
+
__version__ = '0.1.19'
|
10
10
|
|
11
11
|
# reload_modules 함수를 패키지 레벨에서 사용 가능하게 함
|
12
|
+
from pyjallib.logger import Logger
|
13
|
+
from pyjallib.progressEvent import ProgressEvent
|
14
|
+
from pyjallib.exceptions import PyJalLibError, PerforceError, ValidationError, FileOperationError, NamingConfigError, MaxError, UE5Error
|
12
15
|
from pyjallib.namePart import NamePart, NamePartType
|
13
16
|
from pyjallib.naming import Naming
|
14
17
|
from pyjallib.namingConfig import NamingConfig
|