Usage of Claude Code and Codex

claude-code를 사용할때는 하루에 입력토큰이 많아야 2M 로 limit 에 걸릴일이 없었는데,
codex 를 사용하고 부턴 질문 몇개에 금방 5h limit 에 걸려 보니 input token 이 2M가 금방찬다.

https://github.com/ryoppippi/ccusage 는 claude-code/codex 의 사용량 현황을 알려준다.
참고로 사용자 .jsonl 의 토큰 및 캐시 사용 기록 파악한다.
~/.claude/projects/<project>/*.jsonl
~/.codex/sessions/<year>/<month>/<day>/*.jsonl

# claude-code usage
bunx ccusage
bunx ccusage daily
bunx ccusage weekly
bunx ccusage monthly

# codex usage
bunx @ccusage/codex@latest
bunx @ccusage/codex@latest daily
bunx @ccusage/codex@latest weekly
bunx @ccusage/codex@latest monthly

다음과 같이 둘을 비교해보니 한달치 input token 이 claude-code 가 훨씬 적다.

Claude Code: Input 적고 > Cache Read로 대부분 처리 > 저렴
Codex: Input이 크고 > Cache Read 비율이 낮음 > 비쌈

codex reasoning(내부 추론) 과정은 캐시 키를 매번 바꿔서 캐시를 무효화시키는 경향이 있다고 한다.
fast 모드도 비용에 영향을 준다고 함.

다음 설정을 해서 줄여볼 수 있다고 하는데 여전히 input token 이 팍팍 증가한다.
# ~/.codex/config.toml
model_reasoning_effort = "low"
model_reasoning_summary = "concise"
model_verbosity = "low"
[features]
fast_mode = false # /fast 자체가 숨김 처리된다.

automated version pull request

js package.json 이나 rust cargo.toml 등에 소스파일에 version 을 명시하는 언어가 있다.
참고로 golang 소스파일에 버전을 명시하지 않는것이 관례라고 한다.
기능 개발을 하고 버전 태깅전에 별도로 소스 버전(package.json 등)수정하기 위해 브랜치 체크아웃 > commit > push > pr > merge 과정을 거쳐야하는데 불편하다.

[해결방법]
메인 브랜치에 커밋이 쌓이면 도구가 자동으로 release pr 을 생성한다.
(주의) 커밋 메시지는 conventional-commits(https://www.conventionalcommits.org)이 아니면 파싱이 실패하고 release pr 이 생성이 안된다.

1. git tag에서 최신 버전 확인 (예: v1.2.3)
2. 그 tag 이후의 conventional-commits를 분석해 버전 자동 결정
- fix: v1.2.4 (patch)
- feat: v1.3.0 (minor)
- feat!: / BREAKING CHANGE v2.0.0 (major)
3. release pr 생성(package.json 버전 업데이트 + CHANGELOG.md 작성 포함)

.github/workflows/release-please.yml 에 다음과 같이 사용
on:
  push:
    branches:
      - main

permissions:
  contents: write
  pull-requests: write

jobs:
  release-please:
    runs-on: ubuntu-latest
    steps:
      - uses: googleapis/release-please-action@v4
        with:
          # 프로젝트의 언어 타입을 지정 (node, rust, go, python 등)
          release-type: node

github action 을 사용할 수 없는 환경이라면 다음과 같이 cli 로 실행하면 된다.
# bunx: npm 레지스트리에서 패키지를 임시 다운로드 후 바로 실행하는 기능입니다.(npx와 동일)
# --release-type : 프로젝트의 언어 타입을 지정 (node, rust, go, python 등)
# --api-url : 기업 github 사용시 별도 설정 필요
# --graphql-url : 기업 github 사용시 별도 설정 필요
bunx release-please release-pr \
    --repo-url=ysoftman/aaa \
    --target-branch=main \
    --release-type=node \
    --token="$GITHUB_TOKEN"

[참고]
이건 rust 전용으로 머지된 커밋을 확인하여 어떤 crate가 변경되었는지 분석해서 cargo.toml의 버전을 업데이트하고 의존성 버전을 맞춘 pr 을 생성한다.
머지 시 crates.io 배포까지 할 수 있다.

이건 개발자가 태그 생성하면 자동으로 빌드 및 파일 업로드해준다.

filebeat kafka out of acceptable range

log 수집용 filebeat pod 하나가 계속 높은 CPU 사용량을 보인다.
로그를 보면
{"log.level":"error","@timestamp":"2026-04-07T08:12:32.206+0900","log.logger":"kafka","log.origin":{"function":"github.com/elastic/beats/v7/libbeat/outputs/kafka.(*msgRef).dec","file.name":"kafka/client.go","file.line":446},"message":"Kafka publish failed with: kafka server: The timestamp of the message is out of acceptable range","service.name":"filebeat","ecs.version":"1.6.0"}

pod 를 재시작해도 시간이 좀 지나면 다시 CPU 사용량이 90%를 넘어간다.

kafka publish 실패 -> 재시도 무한 반복 -> CPU 급증

broker 레벨 timestamp 설정 확인
kafka-configs --bootstrap-server 카프카호스트:9092 --entity-type brokers --describe --all | rg -i timestamp

log.message.timestamp.type=CreateTime (체크 대상 타입)
log.message.timestamp.after.max.ms=3600000 (1시간, 메시지 시간이 이 시간보다 1시간 초과시 거부)
log.message.timestamp.before.max.ms=9223372036854775807 (사실 무제한, 과거 메시지 모두 허용)

메시지 timestamp 가 before / after 를 벗어난 경우 kafka broker가 timestamp 범위 초과로 메시지를 거부한다.
kafka timestamp 설정은 이상이 없어 보인다.

filebeat.yml 설정에 max_retries가 명시되어 있지 않아 디폴트 3으로 사용된다.
output.kafka:
    enabled: true
    hosts: "${kafka_hosts}"
    topic: "%{[@metadata][topic]}"
    partition.round_robin:
      reachable_only: false
      group_events: 1

max_retries 회수가 제한이 있는데도 CPU가 안 떨어진다는 건 처리해야 할 오래된 로그 라인 자체가 엄청나게 많다는뜻일 수 있다.
registry 초기화 후 모든 로그 파일을 처음부터 다시 읽으면서
이벤트마다 전송 시도 -> 실패 -> retry 3번 (backoff 포함) -> drop -> 다음 이벤트 -> 또 실패
이 사이클이 수십만 건 이상 반복되고 있을 수 있습니다. 시간이 충분히 지나면 결국 CPU는 내려가겠지만, 로그 양이 많으면 오래 걸릴 수 있습니다.

당장 해결을 위해선 pod 재시작 한다.
pod 재시작 후 에도 같은 현상이면 container 상에서 registry(active.dat, log.json, 12345.json)을 지우고 재시작하자. 이때 meta.json 을 지우면 pod 재시작시 crash 가 발생하니 유지해야 된다.
실수로 삭제했다면 meta.json 는 이런 내용으로 다시 생성하면 된다.
{"version":"1"}

filebeat 수집 대상이 *.log* 로 되어 있어 로그 로테이션으로 log 파일들도 매칭이 되고 있는게 근본적인 원인으로 보여 *.log* -> *.log 패턴으로 수정했다.