운영자가 사용자 Spring Boot 앱에 ApiLens agent를 붙일 때 설정하는 JVM 시스템
프로퍼티 명세. agent args(-javaagent:agent.jar=foo=bar)는 사용하지 않는다 —
-D 시스템 프로퍼티로 통일 (디버깅 시 jcmd 등으로 확인 편함).
apilens.debug: 운영 환경에서는 비활성(false) 권장. true 시 PreparedStatement SQL 템플릿이 stderr에 누적되어 스키마 정보가 로그 파일에 남을 수 있음.
java -javaagent:/path/to/apilens-agent.jar \
-Dapilens.service.name=my-app \
-Dapilens.server=http://localhost:8765 \
-jar my-app.jar실제 agent가 인식하는 시스템 프로퍼티는 다음 13개다.
| 옵션 | 기본값 | 타입 | 필수 | 설명 |
|---|---|---|---|---|
apilens.service.name |
— (없으면 agent 비활성) | String | ✅ | 서비스 식별자 (한 호스트 = 한 service.name 권장). 누락/공백 시 agent disabled |
apilens.server |
http://localhost:8765 |
String(URL) | ApiLens server base URL. 끝 슬래시 무관. http/https만 허용 | |
apilens.enabled |
true |
boolean | false 시 agent 완전 비활성 (transport thread도 안 띄움) |
|
apilens.sampling.rate |
1.0 |
double | head-based, [0.0, 1.0]. 범위 외/parse fail → 1.0으로 fallback | |
apilens.batch.max-size |
100 |
int | 한 HTTP POST 당 span 최대 | |
apilens.batch.flush-interval-ms |
1000 |
long | 강제 flush 주기 (ms) | |
apilens.queue.capacity |
10000 |
int | 메모리 buffer 크기. 초과 시 silent drop (호스트 앱 보호 우선) | |
apilens.payload.max-bytes |
65536 |
int | payload 캡처 임계(64KB). 초과 시 truncate=true로 자름 | |
apilens.debug |
false |
boolean | true 시 stderr에 SQL 템플릿 등 상세 로그 누적 — 운영 비권장 |
|
apilens.jdbc.capture-result-set |
false |
boolean | opt-in — true 시 JDBC SELECT 결과(row)를 payload_out에 캡처. 위험 항목 참고 | |
apilens.jdbc.capture-params |
true |
boolean | default ON — PreparedStatement 표준 12종 setter + addBatch 호출에서 파라미터 값을 PAYLOAD IN에 직렬화. 운영망 hot-path 오버헤드 회피용 escape hatch로 false 토글 가능. 비활성 시 advice 자체가 weaving 되지 않아 런타임 비용 0. 자세한 동작은 아래 항목 참고 |
|
apilens.instrument.exclude-packages |
(없음) |
String(콤마 목록) | opt-in — 계측에서 제외할 패키지 prefix 목록(콤마 구분). 미설정/빈 값이면 제외 없음(현 계측 그대로). 지정한 prefix 로 시작하는 클래스는 weaving 대상에서 빠져 span·payload 생성이 없다(weaving 시점 결정, 런타임 비용 0). 자세한 동작·가이드는 아래 항목 참고 | |
apilens.instrument.require-entry-root |
false |
boolean | opt-in — true 시 흐름의 시작점이 진입점(들어오는 요청을 받는 controller 계층)이 아니면 그 흐름 자체를 만들지 않음. 진입점 밖에서 시작되는 흐름이 통째로 사라지므로 기본 꺼짐 (배치 워커가 대표적인 예이고, 메시지 소비자·기동 작업·요청에서 갈라진 별도 스레드도 포함). 자세한 동작은 아래 항목 참고 |
| 입력 | 결과 |
|---|---|
apilens.service.name 누락 또는 공백 |
agent disabled, stderr 한 줄 경고 |
apilens.enabled=false |
agent disabled (조용히) |
apilens.server URL parse fail ("abc") |
agent disabled, stderr 경고 |
apilens.server 비-http/https (ftp://…) |
agent disabled, stderr 경고 |
apilens.sampling.rate=1.5 또는 -0.1 |
1.0으로 fallback, 경고. agent는 enabled 유지 |
apilens.batch.max-size=abc 또는 0 |
100으로 fallback, 경고. agent enabled 유지 |
| 그 외 모든 예외 (premain 자체 fail 포함) | stderr 한 줄, agent 비활성, 호스트 앱 정상 시작 |
- agent는 daemon thread 1개(
apilens-sender)만 띄움 — JVM 종료 막지 않음 - 종료 시 shutdown hook이 큐 잔여 span을 최대 2초 동안 flush 시도
- 모든 외부 의존성(ByteBuddy, Jackson)은 shadow jar에서
io.apilens.agent.shaded.*로 relocate되어 사용자 앱과 클래스 충돌 없음 - HTTP 전송은 JDK
java.net.http.HttpClient사용 — agent는 추가 외부 의존성 0
2xx→ 성공4xx→ drop (재시도 무의미; 잘못된 payload)5xx또는 IO 실패 → 1초 대기 후 1회 재시도. 그래도 실패면 silent drop- exponential backoff / 영속 큐는 향후 예정
agent가 정상 시작하면 stderr에 한 줄 출력:
[ApiLens] ApiLens agent started: service=my-app, server=http://localhost:8765, samplingRate=1.0, batchMaxSize=100
이어서 hello span (operationName=agent.startup, spanKind=INTERNAL) 1건이
서버에 전송됨. 이걸 dashboard에서 확인하면 agent ↔ server 채널이 살아있다는
신호.
curl -s http://localhost:8765/v1/traces?service=my-app | jq
# → traces[0].rootOperation == "agent.startup"서버에 API Key 인증을 켠 경우 위
curl호출에는-H "Authorization: Bearer <키>"가 필요하다. 단 agent 적재 경로(POST /v1/spans)는 무인증 화이트리스트이므로 agent JVM 옵션에는 토큰이 필요 없다 (자세한 내용은 setup.md의 "인증 (선택)" 절 참조).
기본 비활성. 켜면 JDBC executeQuery()가 반환한 ResultSet을 agent가 가로채
모든 row를 미리 메모리에 읽고, caller에게는 같은 내용을 들고 있는 wrapper를
돌려준다. 결과는 trace 상세 화면의 PAYLOAD OUT 자리에 {columns: [...], rows: [[...], ...]}
JSON으로 표시된다.
- wrapper는 표준
ResultSetAPI만 지원:next/getXxx/getMetaData/close/wasNull/getRow/findColumn/is{Before,After}First/First/Last은 정상 동작. driver-specificunwrap(예:OracleResultSet), scrollable navigation(absolute/previous), row update API는SQLFeatureNotSupportedException. - 확인된 안전 조합: MyBatis
TypeHandler, Spring JDBCRowMapper, raw JDBC. - 위험 가능 조합: Hibernate 일부 path가 driver-specific unwrap을 호출. 운영망 활성화 전 staging에서 본인 ORM과 검증 후 켤 것.
- capture 자체 실패 시 agent는 silent drop하지만 underlying ResultSet이 이미 부분 진행된 상태로 caller에 노출됨 — caller가 row 일부를 못 받을 수 있음. 이건 opt-in으로 명시 수용한 위험이다.
- 한 SELECT 당 최대 100 row +
apilens.payload.max-bytes한도(기본 65,536 byte) 중 먼저 닿는 것까지만 capture. 초과 시db.rows_truncated=trueattribute. wasNull()은 마지막getXxx()호출 결과만 추적 (표준 ResultSet 동작).- 변환(예:
getDate(int, Calendar))은 best-effort. 정확한 변환이 필요하면 옵션을 꺼서 raw driver에 위임할 것.
기본 활성. PreparedStatement 의 표준 12종 setter (setString, setInt,
setLong, setDouble, setFloat, setBoolean, setBigDecimal, setDate,
setTime, setTimestamp, setBytes, setNull) 와 addBatch() 호출이
agent 에 후킹되어 trace 상세 화면의 PAYLOAD IN 자리에 JSON 으로 떨어진다.
Spring Data JPA / Spring JDBC / MyBatis 모두 내부적으로 PreparedStatement 를
쓰기 때문에 사실상 모든 DB 호출의 파라미터가 보인다.
| JDBC 타입 | PAYLOAD IN 형식 |
|---|---|
String / Number / Boolean |
toString() 그대로 |
BigDecimal |
toPlainString() (지수 표현 회피) |
byte[] (setBytes) |
[B@<lowercase-hex-prefix>] — 최대 16 bytes(32 hex chars) |
null (setNull 의 value 자체가 null) |
NULL |
java.sql.Date |
ISO-8601 (YYYY-MM-DD) |
java.sql.Time |
ISO-8601 (HH:MM:SS) |
java.sql.Timestamp |
ISO-8601 (YYYY-MM-DDTHH:MM:SS[.fff]) |
java.time.LocalDate/Time/DateTime |
ISO-8601 (defensive — MyBatis TypeHandler 가 종종 사용) |
java.time.Instant |
ISO-8601 (...Z) |
| 그 외 타입 | <unknown:SimpleClassName> (silent fall-through) |
단일 호출의 결과 JSON 모양:
{"1":"42","2":"John","3":"2026-05-14"}addBatch() 가 끼어있으면 batch 묶음으로 직렬화 + db.batch_size=N attribute 부착:
{"batch_size":3,"batch":[{"1":"a"},{"1":"b"},{"1":"c"}]}-Dapilens.jdbc.capture-params=false운영망에서 초당 수만 PreparedStatement 호출이 발생하는 hot-path 가 있고
오버헤드를 0 으로 만들어야 한다면 위 토글로 advice 자체를 끈다. false 로
시작한 JVM 에서는 advice 가 weaving 되지 않으므로 cache 도, setter 후킹도,
addBatch 후킹도 발생하지 않는다.
- 모든 advice 진입점에
try-catch(Throwable)+ silent drop — 호스트 앱 throw 0 단언 (ClassCastException/NoSuchMethodError/VerifyError포함). - 캡처 캐시는
WeakHashMap<PreparedStatement, ...>— statement close 시 자동 회수. execute exit 시점에 명시적 clear 도 호출하므로 정상 경로에서 leak 0. - 비표준 driver-specific setter (
setOracleObject,setPGobject, etc.) 와 12종 외 표준 setter (setObject,setBlob, etc.) 는 본 영역 밖 — 매처 자체가 매치하지 않아 advice 진입 0건.
PAYLOAD IN 본문의 키는 JDBC parameterIndex 의 decimal string 입니다
(예: {"1":"hong","2":"password123"}). 이 구조는 server-side 기본 마스킹 룰
중 이름 기반 룰 (password|passwd|pwd|secret|token) 이 매칭되지 않는다는
의미입니다 — 룰이 키 이름을 fullmatch 로 검사하기 때문입니다.
영향:
| PII 유형 | 마스킹 결과 (현재) |
|---|---|
| 주민등록번호 (RRN), 카드번호 — REGEX 패턴 강한 룰 | ✓ 정상 마스킹 (값 자체가 정규식 매치) |
| password, token, secret — 이름 기반 룰 | ✗ 평문 노출 |
운영자 권장 조치:
- 의심되는 운영망에서는
apilens.jdbc.capture-params=false로 시작 (위 escape hatch 참조). - PreparedStatement 의
?가 사용자 비밀번호 / API 토큰 / 세션 키 같은 컬럼에 바인딩되는 경우, server-side custom masking 룰을 REGEX 형태로 직접 추가 하시기 바랍니다 (예:^[A-Za-z0-9_]{32,}$처럼 값의 형태로 매칭). - 향후 parameterIndex 키 구조에서도 작동하는 마스킹 룰 보강 예정 (column 이름 추적 또는 value heuristic).
이 제약은 capture-params default ON 결정의 결과이며 구현 버그 아닙니다. 단 인지 없이 설치하는 운영자에게 위험할 수 있으므로 본 문단 명시.
기본 비어 있음(제외 없음). 운영자가 계측 자체에서 빼고 싶은 패키지 prefix 를 콤마로 나열하면, 그 prefix 로 시작하는 클래스는 advice weaving 대상에서 제외됩니다.
-Dapilens.instrument.exclude-packages=com.acme.noisy,com.acme.batch위 예시는 com.acme.noisy.* / com.acme.batch.* 로 시작하는 클래스를 계측에서 뺍니다.
- weaving 시점에 결정 — 런타임 비용 0. 제외된 클래스는 advice bytecode 가 아예 합성되지 않으므로, 그 클래스의 메서드가 아무리 자주 호출돼도 span·payload 생성이 일어나지 않고 마스킹·전송 경로도 타지 않습니다. 즉 "런타임에 필터링"이 아니라 "처음부터 안 짜여" 있습니다.
- prefix 시맨틱(경계 아님).
com.acme는com.acme.Foo뿐 아니라com.acme2.Bar도 매치합니다(순수 문자열 prefix). 좁게 지정하려면com.acme.처럼 끝에 점을 붙여 경계를 명확히 하세요. - 미설정/빈 값/공백/후행 콤마 → 제외 없음(현 계측 그대로). 안전 폴백이므로 오타로 빈 값이 들어가도 계측이 조용히 꺼지지 않습니다.
- 잎(leaf) 계층에만 쓰세요. 잡음이 많은 특정 repository/batch 패키지처럼, 빠져도 흐름 해석에 지장이 없는 말단 계층이 대상입니다.
- 하위 계층을 계속 보고 싶으면 그 조상 패키지를 exclude 하지 마세요. 예를 들어 Controller 를 exclude 하면서 Service/Repository 는 계속 계측하면, root Controller 노드가 그래프에서 빠져 흐름이 끊겨 보입니다(고아 노드 자체로 무결성이 깨지진 않지만 — 가장 가까운 계측된 조상이 부모가 됩니다 — UX 상 흐름 파악이 어려워집니다).
- 이 옵션의 목적은 불필요한 계측을 줄여 대시보드 잡음과 저장 부담(span·payload 생성, 적재 시 write lock 보유)을 낮추는 것입니다. 제외한 패키지만큼 그 물리적 부하가 발생하지 않습니다.
- 실제로 얼마나 줄었는지(용량·유실률 변화)는 본인 운영망에서 적용 전·후를 측정해 확인하세요. 트래픽·계측 대상 분포에 따라 달라지므로 문서가 정량 수치를 단정하지 않습니다.
- setup wizard(설치 명령 생성기)에는 노출하지 않는 고급 opt-in 입니다. NAS 등
운영망 JVM 의
-D로 직접 지정하세요. 원격 계측 설정 화면(Services 표의 [계측 설정])에 있는 옵션 문자열 생성기가 이 키를 포함한-D한 줄 조립을 도와줍니다 — 아래 원격 계측 설정 절 참조.
MyBatis mapper 는 이 옵션으로 뺄 수 없습니다.
계측이 걸리는 이름과 화면에 보이는 이름이 다르기 때문입니다. 계측은 라이브러리의
프록시 한 점(org.apache.ibatis.binding.MapperProxy)에 걸리는데, 화면에는 그
프록시가 대신 실행해 준 여러분의 mapper 인터페이스 이름이 찍힙니다. 그래서 화면에
보이는 mapper 이름을 옵션에 적어도 아무 일도 일어나지 않습니다(경고도 뜨지
않습니다).
대신 원격 계측 설정의 gateExcludes 로는 화면에 보이는 mapper 인터페이스 이름
그대로, JVM 재시작 없이 개별 제외할 수 있습니다 — 아래 원격 계측 설정
절과 두 가지 "제외"의 차이 표를 보세요.
통째로 빼려면 org.apache.ibatis.binding.MapperProxy 를 지정해야 합니다. 다만 이
경우 SQL 과 mapper 메서드의 대응이 사라집니다 — 어느 메서드가 그 쿼리를 불렀는지
알 수 없게 됩니다. SQL 자체는 DB 구간에 그대로 남습니다.
Spring Data JPA 를 쓰신다면 repository 계층의 이름이 여러분의 인터페이스가 아니라 프레임워크 구현체로 보일 수 있습니다. 이 경우에도 여러분의 repository 이름으로는 제외 지정이 되지 않습니다. (이 항목은 아직 실환경에서 확인하지 못한 추정입니다 — 자기 환경에서 한 번 확인해 보시기 바랍니다.)
이 옵션은 "그 클래스의 기록을 만들지 않는다"는 뜻이지 "그 아래 호출을 따라가지 않는다"는 뜻이 아닙니다. 흐름의 위쪽을 빼면 그 아래 있던 호출들이 각자 독립된 시작점이 되어, 조각난 흐름이 쏟아질 수 있습니다.
한 표본 시스템의 창 4개에서 기록이 하나뿐인 흐름의 비율이 약 97% 까지 올라간 경우가 있었습니다. 이 값은 그 표본·그 구간의 값이며 일반적인 수치가 아닙니다 — 빼기 전에 계측 분석 화면에서 예상 결과를 먼저 확인하시기 바랍니다. 그 화면이 빼기 전후의 조각남 정도를 미리 계산해 보여 줍니다.
이 조각남을 원천에서 만들지 않으려면 아래
apilens.instrument.require-entry-root(진입점 없는 흐름 만들지 않기) 옵션을 함께
검토하세요.
기본 꺼짐. 켜면 흐름의 시작점이 되려는 기록의 종류가 진입점(들어오는 요청을 받는 controller 계층)이 아니면 그 흐름 자체를 만들지 않습니다.
-Dapilens.instrument.require-entry-root=true계측 제외로 흐름의 위쪽을 빼면 그 아래 호출들이 각자 독립된 시작점이 되어, 기록이 하나뿐인 조각 흐름이 쏟아질 수 있습니다(위 절 참조). 이 옵션은 그 조각을 애초에 만들지 않는 쪽의 레버입니다 — 진입점 없이 시작되려는 흐름은 저장도, 전송도, 본문 생성도 하지 않습니다.
- 켜면 진입점 밖에서 시작되는 흐름이 통째로 사라집니다. 배치 워커(
@Scheduled/@Async류)가 대표적인 예이지만 그것만은 아닙니다. 이것이 이 옵션의 목적이며 의도된 동작입니다 — 그래서 기본값이 꺼짐입니다. 진입점 밖 흐름도 계속 보고 싶다면 켜지 마세요. - 사라지는 범위는 "배치 워커" 보다 넓습니다. 다음도 진입점 밖 시작이라 함께 사라집니다:
- 메시지 소비자 / 이벤트 리스너에서 시작되는 흐름
- 애플리케이션 기동·종료 시점에 도는 초기화 작업
- 서버가 스스로 부르는 내부 작업(캐시 갱신·헬스 점검 등)
- 진입점(controller)에서 시작된 흐름 자체는 영향이 없습니다 — 그 아래 service / repository / mapper / DB 기록은 그대로 남습니다. 다만 요청에서 갈라져 나온 별도 스레드(비동기 실행·별도 스레드 풀에 넘긴 작업)는 그 스레드에서 새로 시작되는 흐름이므로 영향을 받습니다. "요청 처리 중에 일어난 일" 이라고 해서 전부 남는 것은 아닙니다.
- 억제된 시작점 아래에서 이어지는 호출들도 함께 억제됩니다(일관 억제 — 반쪽 흐름이 남지 않습니다).
- agent 시작 알림(
operationName=agent.startup)은 이 옵션과 무관하게 계속 전송됩니다 — 서비스 화면의 agent 버전 표시가 유지됩니다. - 이 옵션은 조각남 한계의 완화이지 소멸이 아닙니다 — 켜지 않으면 동작은 이전과 완전히 같습니다.
- 실제로 얼마나 줄었는지는 본인 운영망에서 적용 전·후를 측정해 확인하세요 — 문서가 정량 수치를 단정하지 않습니다.
- 재시작 없이 켜고 싶다면 아래 원격 계측 설정(
requireEntryRoot)으로도 켤 수 있습니다.
server 에 서비스별 "원하는 계측 설정"을 저장해 두면, agent 가 기록을 보낼 때의 응답에 실려 전달되어 JVM 재시작 없이 적용됩니다. 화면(Services 표의 [계측 설정])에서 설정하거나, 아래 API(curl)로도 설정할 수 있습니다.
# 저장 (전체 교체 — 같은 요청을 몇 번 보내도 결과 동일)
curl -X PUT http://localhost:8765/v1/services/my-app/instrument-config \
-H "Content-Type: application/json" \
-d '{"captureParams": false, "requireEntryRoot": true,
"gateExcludes": ["com.acme.mapper.NoisyMapper"]}'
# 조회 (미설정이면 404)
curl http://localhost:8765/v1/services/my-app/instrument-config
# 철회 (몇 번을 보내도 결과 동일)
curl -X DELETE http://localhost:8765/v1/services/my-app/instrument-configserver 에 API Key 인증을 켠 경우 위 호출에는
-H "Authorization: Bearer <키>"가 필요합니다.
설정할 수 있는 항목은 다음 4가지뿐입니다 (그 외 항목은 이 채널로 설정할 수 없습니다):
| 항목 | 뜻 |
|---|---|
captureParams |
false = JDBC 파라미터 캡처 끄기 (apilens.jdbc.capture-params 의 실행 중 값) |
captureResultSet |
false = JDBC 결과(row) 캡처 끄기 (apilens.jdbc.capture-result-set 의 실행 중 값) |
requireEntryRoot |
true = 진입점 없는 흐름 만들지 않기 켜기 (위 절 참조) |
gateExcludes |
재시작 없이 이름 그대로 개별 제외할 클래스/인터페이스의 정확한 전체 이름 목록 (최대 100개, 항목당 512자) |
- 줄이는 방향만 적용됩니다 — 기준점은 JVM 을 시작할 때 준
-D값입니다. 시작값 이하로 줄이는 지시와 시작값까지 되돌리는 지시는 적용되고, 시작값을 넘어 확대하는 지시는 agent 가 버립니다. 이 판정은 server 가 아니라 agent 안에서 합니다 — server 가 무엇을 보내든 agent 계측이 시작값 이상으로 커지지 않습니다. - 원격으로 끈 것은 영구 설정이 아닙니다 — 게이트 값은 메모리에만 있고 JVM 재시작
시 시작
-D값으로 되돌아갑니다. 영구로 만들려면-D를 바꿔 재시작하세요. - 전파는 기록 전송 응답에 실려 옵니다 — 트래픽이 없는 서비스, 수신 일시정지 중,
또는 억제 옵션으로 기록이 급감한 서비스는 적용이 늦어질 수 있습니다. 늦는 방향은
항상 "예전 계측 상태가 잠시 더 유지되는" 쪽이며, 기록이 흐르기 시작하면 자동
적용됩니다(급하면 JVM 재시작 = 시작
-D값 복원). - 철회(DELETE)해도 agent 에 이미 적용된 값은 되돌아가지 않습니다 — 응답에 설정이 더 이상 실리지 않을 뿐입니다. 되돌리려면 시작값 복귀를 명시한 저장(PUT)을 넣거나 JVM 을 재시작하세요.
- 이 원격 설정은
-D옵션의 기본값을 바꾸는 것이 아닙니다 — 실행 중인 JVM 의 메모리 값만 바꿉니다. 문서의 옵션 표 기본값은 그대로 유효합니다. apilens.jdbc.capture-params=false로 시작한 JVM 은 해당 계측 코드 자체가 심어지지 않으므로, 원격 지시로도 켤 수 없습니다(구조적으로 불가).- 키를 설정하지 않은(무인증 폴백) 환경에서 원격 계측 설정 API 는 LAN 신뢰 전제입니다 — 같은 망의 행위자가 남의 서비스 계측을 줄이는 방향으로 끌 수 있습니다. 최악은 계측 꺼짐(가용성)이지 데이터 탈취가 아닙니다.
계측 제외 옵션 (apilens.instrument.exclude-packages) |
원격 게이트 제외 (원격 계측 설정 gateExcludes) |
|
|---|---|---|
| 판별 기준 | 패키지 접두(클래스가 로드될 때 이름 시작 부분) | 클래스/인터페이스 정확한 전체 이름 — MyBatis mapper 처럼 계측이 걸리는 이름이 화면 이름과 달라도 화면 이름 그대로 |
| 적용 시점 | JVM 재시작 필요 (클래스를 바꿔 심는 시점에만) | 재시작 불요 — 다음 설정 전달 시점부터 |
| 비용 | 제외 대상 런타임 비용 0 (계측 코드 자체가 안 심어짐) | 계측 코드는 남고 진입 즉시 되돌아감 (아주 작은 확인 비용 잔존) |
| 영구성 | -D 라서 재시작 후에도 유지 |
메모리에만 — 재시작 시 사라짐 (server 가 다음 전달에서 다시 적용) |
| 공통 주의 | 흐름의 위쪽(조상)을 빼면 아래 호출이 새 시작점으로 승격해 조각난 흐름이 늘 수 있음 — "진입점 없는 흐름 만들지 않기" 옵션(apilens.instrument.require-entry-root) 병용을 검토 |
(좌동) |
본 절은 운영망에 ApiLens agent 를 적용할 때의 보안 권고를 단일 위치에서 안내합니다.
apilens.debug=true 는 stderr 에 PreparedStatement SQL 템플릿이 누적되어 스키마 정보가 로그 파일에 남을 수 있습니다. 운영 환경에서는 비활성(apilens.debug=false, default) 권장. 디버깅 필요 시 staging 에서만 활성 후 재현 끝나면 즉시 비활성.
apilens.server 는 NAS dogfooding 시 동일 호스트 (http://localhost:8765) 가정. 외부 공개 endpoint 사용 시 TLS 종단 (향후 apilens.server.ca-cert 옵션 추가 예정) 적용 후 사용 권장. 현재는 plain HTTP 만 지원합니다.
ApiLens agent 는 사용자 앱과 같은 JVM 프로세스 안에서 동작합니다 (premain). 별도 sidecar 프로세스 / IPC 호출 없음. 본 가정의 결과로:
- agent 의 외부 의존성 (ByteBuddy, Jackson) 은 모두
io.apilens.agent.shaded.*로 relocate 되어 사용자 앱 classpath 와 충돌 0 - agent 자체 메모리는 사용자 앱 heap 일부 사용 (default queue capacity 10000 span + payload max bytes 65536 기준 약 수십 MB)
- agent 가 OOM / GC 영향을 사용자 앱에 전가하지 않도록 queue 초과 시 silent drop (호스트 앱 보호 우선)
agent 코드 모든 진입점은 try { ... } catch (Throwable t) { silent drop } 패턴 의무. 본 원칙으로 agent 자체 장애가 호스트 앱에 영향 0 보장. 다음 영역 모두 호스트 throw 0 단언:
- 모든 advice 진입점 (
@Advice.OnMethodEnter/@Advice.OnMethodExit) capture-paramsadvice (PreparedStatement 표준 12종 setter + addBatch)capture-result-setwrapper (opt-in, ResultSet wrap)- HTTP 전송 thread (
apilens-senderdaemon) - 종료 hook (shutdown hook 큐 잔여 flush)
PII 마스킹 한계는 위 ## ⚠️ PII 노출 경고 절을 참조하세요. 운영자 권장 의사결정:
- 운영망에 PII 의심 컬럼 (password / token / secret / api-key 등) 이 PreparedStatement 의
?에 바인딩되는지 사전 검토 - 의심 시 즉시
apilens.jdbc.capture-params=false(escape hatch) 적용 — advice weaving 자체 비활성, 호스트 오버헤드 0 - 또는 server-side custom masking 룰을 REGEX 형태 (
^[A-Za-z0-9_]{32,}$등) 로 추가
agent 가 payload 를 server 로 송신할 때 server 의 마스킹 엔진 (apilens-common 공유 엔진) 을 반드시 통과합니다. agent 자체에서 DB 의 payloads 테이블에 직접 INSERT 하거나 마스킹을 우회하는 경로 0건. CI workflow 의 회귀 grep gate 가 본 단언을 자동 검증합니다.
단, 에러 기록의 stack trace 본문(exception.stacktrace 속성)은 payload 가 아니라 span 속성이어서 마스킹 엔진을 지나지 않습니다 — 예외 메시지에 민감 값을 담는 앱이라면 그 값이 가려지지 않은 채 저장될 수 있으니 이 점을 고려하세요.
본 표로 운영자가 자기 환경을 정확히 1개로 매핑할 수 있습니다. 매핑 불가능 시 (예: "운영망인데 PII 의심 + 고부하" 동시) → 보안 우선 row 채택 (운영망 PII 의심).
| 환경 | apilens.jdbc.capture-params |
apilens.jdbc.capture-result-set |
apilens.sampling.rate |
apilens.payload.max-bytes |
apilens.instrument.exclude-packages |
|---|---|---|---|---|---|
| 개발 (local) | true (default) |
true (디버깅용 opt-in) |
1.0 |
65536 (default) |
(없음) |
| 스테이징 | true (default) |
false (default, 검증 후 활성 가능) |
1.0 |
65536 |
(없음) |
| 운영망 일반 | true (default) |
false |
1.0 (저트래픽 가정) |
65536 |
(없음) |
| 운영망 PII 의심 | false (escape hatch) |
false |
1.0 |
65536 |
(없음) |
| 고부하 hot-path | false (오버헤드 0) |
false |
0.1 또는 그 이하 |
16384 (축소) |
잡음 leaf 패키지 예: com.acme.batch,com.acme.noisy |
NAS 디스크 절약을 위한 데이터 보존 기간(retention)은 agent 옵션이 아니라 서버측 설정입니다. 서버 설정 페이지에서 보존 정책을 조정하세요.
- agent 기동 후 사용자 앱 stack trace 에
io.apilens.*등장 0 hit 확인 - agent stderr 에서
Throwable/Exception단어 등장 시 → 즉시 escape hatch (apilens.enabled=false) 적용 + GitHub Issue 등록 권장
- agent 기동 후 24시간 dogfooding 동안 사용자 앱 heap 사용량 base line 대비 +수십 MB 이내 (queue capacity 10000 + payload max bytes 65536 기준)
-
jstat -gcutil <pid> 60000으로 old gen 누적 증가율 측정 — base line 대비 차이 없음 확인 - PreparedStatement 캡처 cache (
WeakHashMap<PreparedStatement, ...>) 가 statement close 시 자동 회수 — leak 0 단언
- agent 미부착 base line 의 controller p95 latency 와 agent 부착 후 p95 latency 차이 < 5%
- 측정 방법: 사용자 앱의 healthcheck endpoint 에 50 req 부하 후 평균 응답시간 비교
- 5% 초과 시 →
apilens.sampling.rate=0.1또는apilens.jdbc.capture-params=false로 튜닝
- PreparedStatement 의
?가 사용자 비밀번호 / API 토큰 / 세션 키 같은 컬럼에 바인딩되는지 사전 검토 - dashboard 의 trace 상세에서 PAYLOAD IN 본문에 평문 PII 노출 0 hit 확인 — 발견 시
apilens.jdbc.capture-params=false적용 후 server-side custom masking 룰 추가 - 자세한 한계는 위
## ⚠️ PII 노출 경고절 참조
-
-Dapilens.enabled=false토글 시 agent 완전 비활성 (transport thread 도 안 띄움) —jps또는 thread dump 로apilens-senderdaemon thread 부재 확인 -
-Dapilens.jdbc.capture-params=false토글 시 advice 자체 비활성 — weaving 0 + cache 0 + 호스트 오버헤드 0 (advice 진입 자체가 발생하지 않음)
위 5 항목 모두 PASS 후 dogfooding sign-off. 임의 1건 fail 시 → "실패한 dogfooding" 으로 GitHub Issue 등록 권장 (NAS OS / Java 버전 / Spring Boot 버전 / agent stderr 마지막 30 line /
apilens.debug=true토글 후 재실행 결과 포함).
- agent args 기반 옵션 파싱 (시스템 프로퍼티와 병행)
- 마스킹 룰 client-side 적용 toggle
- exponential backoff + persistent retry queue
- TLS 인증서 옵션 (
apilens.server.ca-cert, …) - agent 자체 health endpoint
- 계측 include 필터(
exclude-packages의 대응 — 지정 패키지만 계측). 현재는 축소 레버로 exclude 만 제공 - 계측 2차 레버: 최소 duration 필터(짧은 span drop) · INTERNAL/payload-off 토글