
Local logging toolkit routing structured mobile SDK logs to a developer-hosted server; stores per-device/session JSONL, supports redaction, simple tail/curl inspection and agent workflows.
Log4Agent is a local logging toolkit for mobile debugging and coding agents. Mobile SDKs send structured logs to a local server on the developer machine; the server stores JSONL logs by device and session so agents can inspect Android, iOS, Flutter, and KMP runtime logs directly from the terminal.
Mobile debugging often blocks coding agents at the same point: they cannot reliably see device logs.
adb logcat, Xcode console, and Flutter output are noisy and hard to filter consistently.tail or curl.Log4Agent provides that local observation surface. It does not replace production logging, crash reporting, or APM; it is designed for local development, simulators, real-device debugging, and agent workflows.
It has these components:
log4agent-server: a Node.js server that runs on the developer machine and stores logs as JSONL.io.github.icyoung:log4agent: a Kotlin Multiplatform client.io.github.icyoung:log4agent-android-native: an Android native client.Log4Agent Swift Package: an iOS native client.log4agent: a Flutter client.http://10.0.2.2:3100/logs
http://127.0.0.1:3100/logs
Callers can override the full endpoint or configure host/port.
Run the server in foreground. Incoming logs are printed to stdout and written to JSONL:
cd server
npm startPublish the KMP client locally:
cd kmp
./gradlew publishToMavenLocalInstall from npm:
npm install -g log4agent-server
log4agent-server start --port 3100
log4agent-server start --background --port 3100
log4agent-server status
log4agent-server stopForeground:
cd server
npm run start:fg -- --port 3100Background:
cd server
npm run start:bg -- --port 3100
npm run status
npm run stopEnvironment variables:
LOG4AGENT_PORT: server port, default 3100
LOG4AGENT_DIR: JSONL output directory, default .log4agent under the terminal working directoryLOG4AGENT_REDACT: set to false to disable server-side redactionLOG4AGENT_PID_FILE: optional background pid fileLOG4AGENT_OUT_FILE: optional background stdout/stderr fileEndpoints:
GET /healthPOST /sessionsPOST /logsGET /logs?tail=100GET /logs?deviceId=<device>&sessionId=<session>&tail=100Storage layout:
.log4agent/
2026-06-05/
pixel_8/
1749100000000-abcd.session.jsonl
1749100000000-abcd.jsonl
Clients generate a session id when Log4Agent.configure(...) runs and perform one /sessions
handshake for that initialization. Every log line carries app, deviceId, and sessionId, so the
server can route logs by device and session even if the first log arrives before the handshake record.
Client SDK configure(...) calls are idempotent. Repeated calls in the same process keep the existing
session and do not send another /sessions handshake. Pass force = true when a caller intentionally
wants to replace the config and start a new session.
Use Maven Central:
repositories {
google()
mavenCentral()
}
commonMain.dependencies {
implementation("io.github.icyoung:log4agent:0.1.0")
}Or add the local Maven dependency after publishToMavenLocal:
repositories {
mavenLocal()
google()
mavenCentral()
}
commonMain.dependencies {
implementation("io.github.icyoung:log4agent:0.1.0")
}Use defaults:
Log4Agent.configure(enabled = true)
Log4Agent.info(
category = "app.start",
message = "App started",
attributes = mapOf("buildType" to "debug"),
)Override the endpoint:
Log4Agent.configure(
Log4AgentConfig.host(host = "192.168.1.10", port = 3100),
)Use an existing Ktor client:
Log4Agent.configure(
config = Log4AgentConfig(),
httpClient = appHttpClient,
)Disable client-side redaction:
Log4Agent.configure(
Log4AgentConfig(redactionEnabled = false),
)Redaction is enabled by default on both client and server. Built-in rules cover common keys such as
authorization, token, password, secret, cookie, session, and code, including URL query
parameters with those names.
Use Maven Central:
repositories {
google()
mavenCentral()
}
dependencies {
implementation("io.github.icyoung:log4agent-android-native:0.1.0")
}Log4Agent.configure(
Log4AgentConfig.host("10.0.2.2"),
okHttpClient = appOkHttpClient,
)
Log4Agent.info("app.start", "Native Android app started")Default endpoint: http://10.0.2.2:3100/logs.
Use Swift Package Manager:
.package(url: "https://github.com/OWNER/Log4Agent.git", from: "0.1.0")Log4Agent.shared.configure(.init(), session: URLSession.shared)
Log4Agent.shared.info("app.start", "Native iOS app started")Default endpoint: http://127.0.0.1:3100/logs.
Use pub.dev:
dependencies:
log4agent: ^0.1.0Log4Agent.configure(
config: const Log4AgentConfig(),
client: appHttpClient, // package:http Client, optional
);
Log4Agent.info('app.start', 'Flutter app started');Use Dio:
Log4Agent.configure(
config: const Log4AgentConfig(),
dio: appDio,
);Use a custom transport:
Log4Agent.configure(
transport: MyLog4AgentTransport(),
);Flutter defaults to the Android emulator endpoint. Use Log4AgentEndpoint.defaultIosSimulator
or Log4AgentConfig.host(...) when running on iOS simulator or a real device.
This repo includes a standard skill at skills/log4agent.
Install it from GitHub:
python3 ~/.codex/skills/.system/skill-installer/scripts/install-skill-from-github.py --url https://github.com/Icyoung/log4agent/tree/main/skills/log4agentOr install it into an agent skill directory by copying or symlinking that folder, then invoke it as
$log4agent. The skill explains how agents should start the local server, integrate SDKs, and inspect
.log4agent/YYYY-MM-DD/<deviceId>/<sessionId>.jsonl logs.
Codex skill path example:
ln -s /path/to/Log4Agent/skills/log4agent ~/.codex/skills/log4agentClaude skill path example:
ln -s /path/to/Log4Agent/skills/log4agent ~/.agents/skills/log4agentThe default log directory is .log4agent under the directory where log4agent-server was started.
Check server status:
log4agent-server status
curl -sS http://127.0.0.1:3100/healthFind sessions:
find .log4agent -name '*.session.jsonl' -printFind log files:
find .log4agent -name '*.jsonl' ! -name '*.session.jsonl' -printTail the latest logs for one session:
tail -100 .log4agent/YYYY-MM-DD/<deviceId>/<sessionId>.jsonlQuery through the server:
curl -sS 'http://127.0.0.1:3100/logs?tail=100'
curl -sS 'http://127.0.0.1:3100/logs?deviceId=<deviceId>&sessionId=<sessionId>&tail=100'When debugging a specific user action, first find the latest .session.jsonl, then tail the matching <sessionId>.jsonl in the same directory.
Log4Agent is a local logging toolkit for mobile debugging and coding agents. Mobile SDKs send structured logs to a local server on the developer machine; the server stores JSONL logs by device and session so agents can inspect Android, iOS, Flutter, and KMP runtime logs directly from the terminal.
Mobile debugging often blocks coding agents at the same point: they cannot reliably see device logs.
adb logcat, Xcode console, and Flutter output are noisy and hard to filter consistently.tail or curl.Log4Agent provides that local observation surface. It does not replace production logging, crash reporting, or APM; it is designed for local development, simulators, real-device debugging, and agent workflows.
It has these components:
log4agent-server: a Node.js server that runs on the developer machine and stores logs as JSONL.io.github.icyoung:log4agent: a Kotlin Multiplatform client.io.github.icyoung:log4agent-android-native: an Android native client.Log4Agent Swift Package: an iOS native client.log4agent: a Flutter client.http://10.0.2.2:3100/logs
http://127.0.0.1:3100/logs
Callers can override the full endpoint or configure host/port.
Run the server in foreground. Incoming logs are printed to stdout and written to JSONL:
cd server
npm startPublish the KMP client locally:
cd kmp
./gradlew publishToMavenLocalInstall from npm:
npm install -g log4agent-server
log4agent-server start --port 3100
log4agent-server start --background --port 3100
log4agent-server status
log4agent-server stopForeground:
cd server
npm run start:fg -- --port 3100Background:
cd server
npm run start:bg -- --port 3100
npm run status
npm run stopEnvironment variables:
LOG4AGENT_PORT: server port, default 3100
LOG4AGENT_DIR: JSONL output directory, default .log4agent under the terminal working directoryLOG4AGENT_REDACT: set to false to disable server-side redactionLOG4AGENT_PID_FILE: optional background pid fileLOG4AGENT_OUT_FILE: optional background stdout/stderr fileEndpoints:
GET /healthPOST /sessionsPOST /logsGET /logs?tail=100GET /logs?deviceId=<device>&sessionId=<session>&tail=100Storage layout:
.log4agent/
2026-06-05/
pixel_8/
1749100000000-abcd.session.jsonl
1749100000000-abcd.jsonl
Clients generate a session id when Log4Agent.configure(...) runs and perform one /sessions
handshake for that initialization. Every log line carries app, deviceId, and sessionId, so the
server can route logs by device and session even if the first log arrives before the handshake record.
Client SDK configure(...) calls are idempotent. Repeated calls in the same process keep the existing
session and do not send another /sessions handshake. Pass force = true when a caller intentionally
wants to replace the config and start a new session.
Use Maven Central:
repositories {
google()
mavenCentral()
}
commonMain.dependencies {
implementation("io.github.icyoung:log4agent:0.1.0")
}Or add the local Maven dependency after publishToMavenLocal:
repositories {
mavenLocal()
google()
mavenCentral()
}
commonMain.dependencies {
implementation("io.github.icyoung:log4agent:0.1.0")
}Use defaults:
Log4Agent.configure(enabled = true)
Log4Agent.info(
category = "app.start",
message = "App started",
attributes = mapOf("buildType" to "debug"),
)Override the endpoint:
Log4Agent.configure(
Log4AgentConfig.host(host = "192.168.1.10", port = 3100),
)Use an existing Ktor client:
Log4Agent.configure(
config = Log4AgentConfig(),
httpClient = appHttpClient,
)Disable client-side redaction:
Log4Agent.configure(
Log4AgentConfig(redactionEnabled = false),
)Redaction is enabled by default on both client and server. Built-in rules cover common keys such as
authorization, token, password, secret, cookie, session, and code, including URL query
parameters with those names.
Use Maven Central:
repositories {
google()
mavenCentral()
}
dependencies {
implementation("io.github.icyoung:log4agent-android-native:0.1.0")
}Log4Agent.configure(
Log4AgentConfig.host("10.0.2.2"),
okHttpClient = appOkHttpClient,
)
Log4Agent.info("app.start", "Native Android app started")Default endpoint: http://10.0.2.2:3100/logs.
Use Swift Package Manager:
.package(url: "https://github.com/OWNER/Log4Agent.git", from: "0.1.0")Log4Agent.shared.configure(.init(), session: URLSession.shared)
Log4Agent.shared.info("app.start", "Native iOS app started")Default endpoint: http://127.0.0.1:3100/logs.
Use pub.dev:
dependencies:
log4agent: ^0.1.0Log4Agent.configure(
config: const Log4AgentConfig(),
client: appHttpClient, // package:http Client, optional
);
Log4Agent.info('app.start', 'Flutter app started');Use Dio:
Log4Agent.configure(
config: const Log4AgentConfig(),
dio: appDio,
);Use a custom transport:
Log4Agent.configure(
transport: MyLog4AgentTransport(),
);Flutter defaults to the Android emulator endpoint. Use Log4AgentEndpoint.defaultIosSimulator
or Log4AgentConfig.host(...) when running on iOS simulator or a real device.
This repo includes a standard skill at skills/log4agent.
Install it from GitHub:
python3 ~/.codex/skills/.system/skill-installer/scripts/install-skill-from-github.py --url https://github.com/Icyoung/log4agent/tree/main/skills/log4agentOr install it into an agent skill directory by copying or symlinking that folder, then invoke it as
$log4agent. The skill explains how agents should start the local server, integrate SDKs, and inspect
.log4agent/YYYY-MM-DD/<deviceId>/<sessionId>.jsonl logs.
Codex skill path example:
ln -s /path/to/Log4Agent/skills/log4agent ~/.codex/skills/log4agentClaude skill path example:
ln -s /path/to/Log4Agent/skills/log4agent ~/.agents/skills/log4agentThe default log directory is .log4agent under the directory where log4agent-server was started.
Check server status:
log4agent-server status
curl -sS http://127.0.0.1:3100/healthFind sessions:
find .log4agent -name '*.session.jsonl' -printFind log files:
find .log4agent -name '*.jsonl' ! -name '*.session.jsonl' -printTail the latest logs for one session:
tail -100 .log4agent/YYYY-MM-DD/<deviceId>/<sessionId>.jsonlQuery through the server:
curl -sS 'http://127.0.0.1:3100/logs?tail=100'
curl -sS 'http://127.0.0.1:3100/logs?deviceId=<deviceId>&sessionId=<sessionId>&tail=100'When debugging a specific user action, first find the latest .session.jsonl, then tail the matching <sessionId>.jsonl in the same directory.