원클릭으로
tracing
Rust tracing + tracing-subscriber 구조화 로깅 - 초기화, EnvFilter, 매크로,
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Rust tracing + tracing-subscriber 구조화 로깅 - 초기화, EnvFilter, 매크로,
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Spring Security 5.5.x + jjwt 0.10.7 레거시 JWT 인증 - WebSecurityConfigurerAdapter, OncePerRequestFilter, javax.servlet 환경
Spring Boot 3.x + Spring Security 6.x + jjwt 0.12.x 기반 모던 JWT 인증 패턴. SecurityFilterChain Bean, 람다 DSL, jakarta.servlet, Virtual Threads 적용
Unity 6 LTS 2D 모바일 게임용 uGUI 시스템 전문 스킬. Canvas/RectTransform/TextMeshPro, 모바일 UI 패턴(팝업·무한 스크롤·광고·IAP), 성능 최적화, UI Toolkit과의 선택 기준 포함.
아크라시아(akrasia, 자제력 없음) 학술 논쟁의 핵심 구도와 주요 연구자·문헌을 빠르게 파악할 수 있는 도메인 지식 스킬. 도덕윤리교육 전공 대학원생(석/박사)이 학위논문·KCI 투고·세미나 준비 시 고대–현대–한국 학계–도덕심리학 흐름을 한 번에 짚도록 구성. <example>사용자: "아리스토텔레스의 propeteia와 astheneia 구분을 인용하려는데 출처를 알려줘"</example> <example>사용자: "데이비슨이 의지박약을 어떻게 가능하다고 봤는지 핵심 논증을 정리해줘"</example> <example>사용자: "한국 도덕교육 학계에서 아크라시아 다룬 논문 있어?"</example>
아리스토텔레스 『니코마코스 윤리학』에서 akrasia(자제력없음)와 akolasia(무절제)의 5축 차이를 정밀하게 정리한 학위논문 자료 스킬. NE VII.4 1147b20-1148b14, VII.8 1150b29-1151a28, III.10-12 1117b23-1119b18 절별 분해와 표준 학자 해석(Bostock, Broadie-Rowe, Pakaluk, Hursthouse, Charles 등)을 포함. 도덕교육 적용을 위한 두 상태 차이의 함의 및 한국어 번역어 처리 권장안 제공. <example>사용자: "akrates와 akolastos를 prohairesis 측면에서 어떻게 구분해야 하나요?"</example> <example>사용자: "NE VII.4의 ἁπλῶς akrasia가 akolasia와 어떻게 갈라지는지 절별 분해해주세요"</example> <example>사용자: "Hursthouse의 연속체 모델을 도덕교육 적용 절에서 어떻게 활용할 수 있나요?"</example>
한국 위기 대응 자원(자살·자해·정신건강·여성·청소년·노인·다문화) 핫라인과 앱·챗봇 안전 가드 응답 패턴 종합. 꿈 해몽·정신건강 앱 등 자가 진단/감정 콘텐츠 도메인에서 위험 신호 포착 시 안전한 자원 안내 문구를 작성할 때 참조. <example>사용자: "꿈 해몽 앱에 위기 안내 문구를 어떻게 넣을까?"</example> <example>사용자: "한국에서 자살예방 핫라인 번호가 어떻게 바뀌었지?"</example> <example>사용자: "정신건강 챗봇 안전 가드 응답 템플릿을 짜줘"</example>
| name | tracing |
| description | Rust tracing + tracing-subscriber 구조화 로깅 - 초기화, EnvFilter, 매크로, |
소스: https://docs.rs/tracing/latest/tracing/ | https://docs.rs/tracing-subscriber/latest/tracing_subscriber/ 검증일: 2026-06-20
주의: tracing 0.1.x / tracing-subscriber 0.3.x 기준. tracing 0.2는 미출시 상태이며, 향후 API 변경 가능성이 있으므로 실제 프로젝트의 Cargo.lock 버전을 확인할 것.
[dependencies]
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
주요 feature flag (tracing-subscriber):
| Feature | 설명 |
|---|---|
env-filter | EnvFilter (RUST_LOG 환경변수 파싱) |
json | JSON 형식 로그 출력 (fmt::format::Json) |
fmt | 포맷팅 레이어 (기본 활성화) |
registry | Registry subscriber (기본 활성화) |
use tracing_subscriber::EnvFilter;
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info")))
.init();
tracing_subscriber::registry()는 여러 Layer를 .with()로 조합할 수 있는 Subscriber 구현체다.
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
tracing_subscriber::registry()
.with(EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,my_crate=debug")))
.with(tracing_subscriber::fmt::layer())
.init();
SubscriberExt 트레이트: registry()에 .with() 메서드 제공SubscriberInitExt 트레이트: .init() / .try_init() 메서드 제공.init()은 글로벌 subscriber를 설정하며, 프로세스당 1회만 호출 가능tracing_subscriber::registry()
.with(EnvFilter::new("info"))
.with(tracing_subscriber::fmt::layer().json())
.init();
EnvFilter는 RUST_LOG 환경변수를 파싱하여 로그 레벨을 제어한다.
# 전역 info, 특정 크레이트만 debug
RUST_LOG=info,my_crate=debug cargo run
# 특정 모듈의 특정 타깃
RUST_LOG=my_crate::api=trace cargo run
# 스팬 필드 기반 필터링
RUST_LOG="info,[my_span]=debug" cargo run
디렉티브 문법:
| 패턴 | 의미 |
|---|---|
info | 전역 INFO 이상 |
my_crate=debug | my_crate 크레이트만 DEBUG 이상 |
my_crate::module=trace | 특정 모듈만 TRACE 이상 |
tower_http=debug | tower-http 내부 로그 활성화 |
코드에서 직접 설정:
// 기본값 fallback 패턴
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,tower_http=debug"));
use tracing::{info, warn, error, debug, trace};
info!("서버 시작");
warn!("설정 파일 없음, 기본값 사용");
error!("데이터베이스 연결 실패");
매크로의 첫 번째 인자들로 key = value 형태의 구조화 필드를 전달한다. 메시지 문자열은 마지막에 위치한다.
// key = value 형태
info!(port = 3000, host = "0.0.0.0", "서버 시작");
// 변수명과 필드명이 같으면 축약 가능
let port = 3000;
info!(port, "서버 시작"); // port = 3000
// % 접두사: Display 트레이트로 기록
// ? 접두사: Debug 트레이트로 기록
info!(user = %user_id, details = ?request, "요청 수신");
// 빈 필드 (나중에 스팬에서 채울 때)
info!(result = tracing::field::Empty, "처리 시작");
let user = "alice";
info!("사용자 {} 로그인", user); // 포맷 문자열
info!(user, "로그인 처리 완료"); // 구조화 필드 + 메시지
info!(user, action = "login", "완료"); // 복수 필드 + 메시지
함수 진입/종료 시 자동으로 스팬을 생성한다. tracing 크레이트에 포함되어 있다.
use tracing::instrument;
#[instrument]
async fn handle_request(user_id: u64, action: String) {
// 자동으로 span 생성: handle_request{user_id=123, action="create"}
info!("요청 처리 중");
}
함수의 모든 인자가 기본적으로 스팬 필드로 기록된다.
민감한 정보나 Display/Debug를 구현하지 않은 인자를 제외한다.
#[instrument(skip(db, password))]
async fn create_user(
db: &DatabasePool,
username: String,
password: String,
) -> Result<User, Error> {
// span: create_user{username="alice"}
// db, password는 기록되지 않음
todo!()
}
skip_all: 모든 인자를 제외한다.
#[instrument(skip_all)]
async fn internal_process(data: LargeStruct) {
todo!()
}
추가 필드를 정의하거나 인자 필드를 재정의한다.
#[instrument(skip(req), fields(method = %req.method(), uri = %req.uri()))]
async fn handle(req: Request) -> Response {
// span: handle{method=GET, uri=/api/users}
todo!()
}
Empty 필드를 선언하고 함수 내에서 나중에 채울 수 있다:
#[instrument(fields(result))]
async fn compute(input: u64) -> u64 {
let output = input * 2;
tracing::Span::current().record("result", output);
output
}
#[instrument(
name = "custom_span_name", // 스팬 이름 지정 (기본: 함수명)
level = "debug", // 스팬 레벨 (기본: INFO)
target = "my_crate::api", // 로그 타깃 지정
err, // Result::Err 시 자동으로 error 이벤트 기록
ret, // 반환값을 자동 기록
)]
async fn my_function() -> Result<String, Error> {
todo!()
}
err: 함수가 Result를 반환할 때, Err 케이스를 ERROR 레벨로 기록err(level = "warn"): err 기록 레벨 변경ret: 반환값을 DEBUG 레벨로 기록ret(level = "info"): ret 기록 레벨 변경스팬은 작업의 시작과 끝을 나타내는 구간이다. 이벤트(info! 등)는 특정 시점의 기록이고, 스팬은 기간(duration)을 나타낸다. 스팬은 중첩되어 트리 구조를 형성한다.
use tracing::{info_span, debug_span, warn_span, span, Level};
let span = info_span!("request", method = "GET", path = "/api");
let span = debug_span!("db_query", table = "users");
let span = span!(Level::INFO, "custom_span", key = "value");
// 방법 1: .enter() - 동기 코드 전용. Drop 시 자동 종료.
let span = info_span!("sync_work");
let _guard = span.enter(); // _guard가 drop될 때까지 이 스팬 안
do_sync_work();
// _guard drop -> 스팬 종료
// 방법 2: .in_scope() - 클로저 범위 동안 스팬 활성화
let result = info_span!("compute").in_scope(|| {
expensive_computation()
});
주의:
.enter()는 async 함수 내에서 사용하면 안 된다..enter()가 반환하는 드롭 가드는 스코프를 벗어날 때 드롭되어야 스팬이 종료되는데, async에서.await를 만나면 스코프를 양보하면서도 드롭 가드가 드롭되지 않는다. 결과적으로 스팬이 종료되지 않은 상태로 다른 태스크가 실행된다. 멀티스레드 런타임에서는 재개 시 다른 스레드에서 실행되는 추가 문제도 있다. async 코드에서는#[instrument]또는.instrument()메서드를 사용한다.
use tracing::Instrument;
async fn parent_task() {
let span = info_span!("child_task", task_id = 42);
// .instrument()로 Future에 스팬을 부착
some_async_work()
.instrument(span)
.await;
}
let span = info_span!("process", result = tracing::field::Empty);
let _guard = span.enter();
let value = compute();
span.record("result", value);
tower-http의 TraceLayer를 사용하여 Axum 요청/응답을 자동 추적한다.
[dependencies]
axum = "0.8"
tower-http = { version = "0.6", features = ["trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
use axum::{routing::get, Router};
use tower_http::trace::TraceLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,tower_http=debug")))
.with(tracing_subscriber::fmt::layer())
.init();
let app = Router::new()
.route("/", get(root))
.layer(TraceLayer::new_for_http());
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
tracing::info!("서버 시작: 0.0.0.0:3000");
axum::serve(listener, app).await.unwrap();
}
async fn root() -> &'static str {
tracing::info!("루트 핸들러 호출");
"Hello, World!"
}
use tower_http::trace::{TraceLayer, DefaultMakeSpan, DefaultOnResponse};
use tracing::Level;
let trace_layer = TraceLayer::new_for_http()
.make_span_with(DefaultMakeSpan::new().level(Level::INFO))
.on_response(DefaultOnResponse::new().level(Level::INFO));
// 주의: axum::serve() 없이 단독 사용 시 Router 타입 추론 실패 가능. 타입 명시 권장.
let app: Router = Router::new()
.route("/", get(root))
.layer(trace_layer);
use axum::extract::Path;
use tracing::instrument;
#[instrument(skip_all, fields(user_id))]
async fn get_user(Path(user_id): Path<u64>) -> String {
tracing::Span::current().record("user_id", user_id);
tracing::info!("사용자 조회");
format!("User {}", user_id)
}
use tracing_subscriber::{
layer::SubscriberExt,
util::SubscriberInitExt,
EnvFilter,
};
pub fn init_tracing() {
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
EnvFilter::new(
"info,my_app=debug,tower_http=debug,sqlx=warn"
)
});
tracing_subscriber::registry()
.with(env_filter)
.with(tracing_subscriber::fmt::layer()
.with_target(true) // 로그 타깃 표시
.with_thread_ids(false) // 스레드 ID 숨김
.with_file(true) // 파일명 표시
.with_line_number(true) // 라인 번호 표시
)
.init();
}