FIX : 로깅 모듈 버그 2종 수정#70
Conversation
import.meta.env.DEV는 vite build 시 명령(command)을 기준으로 false로 치환되어 --mode development로 빌드해도 debugLog가 항상 묵음이 됨. import.meta.env.MODE는 --mode 플래그 값을 그대로 반영하므로 build:local(--mode development)에서 debug/info 로그가 정상 출력됨. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> docs(logger): add comment explaining DEV vs MODE distinction import.meta.env.DEV가 vite build 시 항상 false가 되는 이유와 MODE로 교체한 배경을 주석으로 명시함. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
기존에는 isPlainObject가 false인 객체(클래스 인스턴스, chrome API 객체 등)를 String()으로 변환해 "[object Object]"로 로깅되는 문제가 있었음. Object.getOwnPropertyNames으로 교체해 non-enumerable 프로퍼티까지 추출하도록 수정. 이를 통해 chrome.runtime.lastError.message(non-enumerable)나 DOMException 등 Chrome 내부 객체도 의미있는 정보로 출력됨. 클래스 인스턴스에는 "[type]" 힌트를 추가해 타입 파악을 용이하게 함. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request updates the IS_DEV flag to use import.meta.env.MODE for more reliable environment detection during local builds and enhances the sanitizeValue function to better handle non-plain objects and class instances by extracting properties and adding type hints. The review feedback suggests specifically handling RegExp objects to improve debug output and wrapping property access in a try-catch block to prevent potential crashes when encountering throwing getters or Proxy objects.
| const allKeys = isPlainObject(value) | ||
| ? Object.keys(value) | ||
| : Object.getOwnPropertyNames(value as object); |
There was a problem hiding this comment.
RegExp 객체는 일반 객체처럼 속성을 순회하는 것보다 toString()을 통해 정규식 패턴을 보여주는 것이 디버깅에 훨씬 유용합니다. 현재 로직에서는 RegExp가 non-plain 객체로 분류되어 lastIndex와 같은 내부 속성만 출력되는 문제가 있으므로, 순회 전에 별도로 처리하는 것을 권장합니다.
if (value instanceof RegExp) {
return sanitizeString(value.toString());
}
const allKeys = isPlainObject(value)
? Object.keys(value)
: Object.getOwnPropertyNames(value as object);| const sanitizedObject = keys.reduce<Record<string, unknown>>((acc, key) => { | ||
| acc[key] = SENSITIVE_KEY_PATTERN.test(key) | ||
| ? REDACTED | ||
| : sanitizeValue(entryValue, depth + 1, seen); | ||
| : sanitizeValue((value as Record<string, unknown>)[key], depth + 1, seen); | ||
| return acc; | ||
| }, {}); |
There was a problem hiding this comment.
로깅 대상 객체가 Proxy이거나 특정 상황에서 예외를 던지는 getter를 가지고 있을 경우(예: 일부 브라우저 내부 객체), 속성 접근 시 로거 자체가 중단될 수 있습니다. 안전한 로깅을 위해 속성 값을 읽어올 때 try-catch로 보호하고, 민감한 키인 경우 접근 자체를 건너뛰도록 개선하는 것이 좋습니다.
| const sanitizedObject = keys.reduce<Record<string, unknown>>((acc, key) => { | |
| acc[key] = SENSITIVE_KEY_PATTERN.test(key) | |
| ? REDACTED | |
| : sanitizeValue(entryValue, depth + 1, seen); | |
| : sanitizeValue((value as Record<string, unknown>)[key], depth + 1, seen); | |
| return acc; | |
| }, {}); | |
| const sanitizedObject = keys.reduce<Record<string, unknown>>((acc, key) => { | |
| if (SENSITIVE_KEY_PATTERN.test(key)) { | |
| acc[key] = REDACTED; | |
| return acc; | |
| } | |
| try { | |
| acc[key] = sanitizeValue((value as Record<string, unknown>)[key], depth + 1, seen); | |
| } catch (e) { | |
| acc[key] = "[Error accessing property]"; | |
| } | |
| return acc; | |
| }, {}); |
RegExp 는 패턴 문자열로 바로 로깅되도록 분기했습니다. throwing getter, Proxy, inspect 불가능한 객체로 인해 로거가 다시 실패하지 않도록 property access 와 type 조회를 방어하고 fallback 값을 추가했습니다.
|
@codex issue fixed review agian |
|
@gemini-code-assist issue fixed review agian |
There was a problem hiding this comment.
Code Review
This pull request improves the logger utility by refining environment detection and enhancing object sanitization. It switches to import.meta.env.MODE to ensure debug logs are correctly enabled in local builds and updates sanitizeValue to better handle non-plain objects, including non-enumerable properties and class type hints. A bug was identified in the object sanitization logic where empty plain objects would incorrectly trigger the UNINSPECTABLE_OBJECT fallback; a suggestion was provided to ensure empty plain objects are returned correctly and to optimize type name retrieval.
| if (!plainObject) { | ||
| const typeName = getObjectTypeName(value as object); | ||
| if (typeName) { | ||
| sanitizedObject["[type]"] = typeName; | ||
| } | ||
| } | ||
|
|
||
| if (allKeys.length === 0 && Object.keys(sanitizedObject).length === 0) { | ||
| return getObjectTypeName(value as object) ?? UNINSPECTABLE_OBJECT; | ||
| } |
There was a problem hiding this comment.
빈 일반 객체({})를 로깅할 때 [Uninspectable Object]가 반환되는 버그가 있습니다. plainObject가 true인 경우에는 이 폴백 로직을 타지 않고 빈 객체({})를 반환해야 합니다. 또한 getObjectTypeName을 중복 호출하지 않도록 변수에 저장하여 재사용하는 것이 좋습니다.
| if (!plainObject) { | |
| const typeName = getObjectTypeName(value as object); | |
| if (typeName) { | |
| sanitizedObject["[type]"] = typeName; | |
| } | |
| } | |
| if (allKeys.length === 0 && Object.keys(sanitizedObject).length === 0) { | |
| return getObjectTypeName(value as object) ?? UNINSPECTABLE_OBJECT; | |
| } | |
| let typeName: string | null = null; | |
| if (!plainObject) { | |
| typeName = getObjectTypeName(value as object); | |
| if (typeName) { | |
| sanitizedObject["[type]"] = typeName; | |
| } | |
| } | |
| if (!plainObject && allKeys.length === 0 && Object.keys(sanitizedObject).length === 0) { | |
| return typeName ?? UNINSPECTABLE_OBJECT; | |
| } |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d9f34c6c02
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (allKeys.length === 0 && Object.keys(sanitizedObject).length === 0) { | ||
| return getObjectTypeName(value as object) ?? UNINSPECTABLE_OBJECT; | ||
| } |
There was a problem hiding this comment.
Return empty plain objects as objects
sanitizeValue now converts any object with zero own keys into the "[Uninspectable Object]" string, which regresses normal {} logging behavior and can violate the Record<string, unknown> contract of getErrorLogDetails when a plain empty object is thrown. In that case callers that spread the result (e.g. response-parse error logging paths) can get string-character keys instead of a structured object, so this fallback should be limited to non-plain/uninspectable host objects only.
Useful? React with 👍 / 👎.
#주요 변경점
로컬 빌드 시 디버그 로그 미노출 버그 수정
Chrome 내부 객체 로깅 오류 수정
1. 로컬 빌드 환경에서 디버그 로그가 출력되지 않던 버그
문제
로컬 테스트용으로 빌드(
build:local)한 확장 프로그램에서 logger 모듈의 debugLog / infoLog가 전혀 출력되지 않아 디버깅이 사실상 불가능한 상태였습니다.원인
로그 출력 여부를 결정하는
IS_DEV값을 빌드 도구(Vite)가 제공하는 개발 모드 플래그로 판단하고 있었는데, 이 플래그는 로컬 빌드 시 항상 false로 처리되는 특성이 있었습니다. 즉, 아무리 로컬용 빌드 명령을 써도 프로덕션처럼 동작했습니다.결정
빌드 도구 플래그 대신 빌드 스크립트에 명시된 mode 문자열을 직접 비교하도록 변경했습니다.
build:local스크립트에--mode development가 붙어있는 한 IS_DEV가 정확히 true가 됩니다.2. Chrome 내부 객체가
[object Object]로 깨져 보이던 버그문제
chrome.runtime.lastError같은 Chrome 브라우저 내부 객체를 로그에 찍으면[object Object]로만 출력되어 어떤 에러인지 전혀 알 수 없었습니다.원인
로거가 "일반 객체가 아닌 것"을 단순 문자열 변환(
String())으로 처리했는데,Chrome 내부 객체는 프로퍼티(예:
.message)가 숨겨진 방식으로 저장되어 있어일반적인 방법으로는 읽히지 않았습니다.
결정
숨겨진 프로퍼티까지 읽는 방식(
Object.getOwnPropertyNames)으로 교체해Chrome 내부 객체도 의미있는 내용으로 출력되도록 수정했습니다.
또한 클래스 인스턴스는
"[type]": "클래스명"힌트를 함께 출력해 타입 파악을 쉽게 했습니다.앞으로의 협업 포인ㄴ트
pnpm run build:local→ debug/info 로그 정상 출력pnpm run build→ 로그 자동 묵음 (기존과 동일)debugLog / infoLog / warnLog / errorLog사용(console.log 직접 호출 금지 — 민감 정보가 배포 빌드에 노출될 위험)
관리 포인트
build:local스크립트에 반드시--mode development가 유지되어야 합니다.이 플래그가 빠지면 로컬 빌드에서도 다시 로그가 묵음이 됩니다.
변경 범위
src/utils/logger.ts단일 파일, 프로덕션 동작 변경 없음LINKU 화이띵