This guide shows how to run Elasticsearch, Logstash, and Kibana locally for JCT UDP or TCP events. It uses the files already in this repository:
docker-compose.ymllogstash.conf
For stack analysis patterns (KQL, Dev Tools queries, trends, top classes/methods), see:
README-analysis-ELK.md
- Architecture
- What You Get
- 1) Start the ELK Stack
- 2) Open Kibana UI
- 3) Send Data from JCT via UDP or TCP
- 4) Configure Kibana Data View
- 5) Explore Logs in Discover
- Quick UDP Smoke Test (Without JCT)
- Quick TCP Smoke Test (Without JCT)
- Troubleshooting
- Stop the Stack
Your JVM (instrumented app)
┌─────────────────────────────────────────────────────────────┐
│ │
│ ┌──────────────────────┐ ┌───────────────────────┐ │
│ │ JCT Agent │ │ Recorder │ │
│ │ -javaagent:jct.jar │────▶│ records call stacks │ │
│ └──────────────────────┘ └─────────────┬─────────┘ │
│ │ │
│ ┌──────────────────▼────────────┐ │
│ │ Stack Processor │ │
│ │ AsyncUdpStackProcessor or │ │
│ │ AsyncTcpStackProcessor │ │
│ └──────────────────┬────────────┘ │
└─────────────────────────────────────────────┼───────────────┘
│
UDP / TCP ▼ port 9999
┌────────────────────────────────────────┐
│ Docker Compose │
├────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌───────────────┐ │
│ │ Logstash │─▶│Elasticsearch │ │
│ │ UDP + TCP │ │ jct-events-* │ │
│ │ :9999 │ │ :9200 │ │
│ └──────────────┘ └───────┬───────┘ │
│ │ │
│ ┌────────────▼────────┐ │
│ │ Kibana │ │
│ │ Discover / Lens / │ │
│ │ Dev Tools :5601 │ │
│ └─────────────────────┘ │
└────────────────────────────────────────┘
- Elasticsearch on
http://localhost:9200 - Logstash UDP input on
localhost:9999 - Logstash TCP input on
localhost:9999 - Kibana UI on
http://localhost:5601 - Index pattern written by Logstash:
jct-events-%{+YYYY.MM.dd}
From the repository root:
docker compose up -d
docker compose psOptional health checks:
curl -fsS http://localhost:9200 >/dev/null && echo "Elasticsearch is up"
curl -fsS http://localhost:5601 >/dev/null && echo "Kibana is up"
curl -fsS http://localhost:9600 >/dev/null && echo "Logstash API is up"Open:
http://localhost:5601
If Kibana is still booting, wait a few seconds and refresh.
Use a UDP processor config such as:
src/test/resources/integration/test-config-asyncudp.yaml- or
doc/config-sample-helloworld-udp.yaml
Or use TCP processor config such as:
src/test/resources/integration/test-config-asynctcp.yaml- or
doc/config-sample-helloworld-tcp.yaml
The required processor fields are:
processor.fullQualifiedClass: de.marcelsauer.profiler.processor.udp.AsyncUdpStackProcessorprocessor.udpHost: localhostprocessor.udpPort: 9999
For TCP, use:
processor.fullQualifiedClass: de.marcelsauer.profiler.processor.tcp.AsyncTcpStackProcessorprocessor.tcpHost: localhostprocessor.tcpPort: 9999processor.tcpConnectTimeoutMillis: 1000processor.tcpReconnectDelayMillis: 1000
Example run (hello world sample):
cd sample_application && mvn clean packageThen start with agent and UDP output:
java \
-javaagent:"${PWD}/target/java-code-tracer-1.0-SNAPSHOT-jar-with-dependencies.jar" \
-Djct.loglevel=INFO \
-Djct.config="${PWD}/doc/config-sample-helloworld-udp.yaml" \
-Djct.logDir=/tmp/jct \
-noverify \
-jar "${PWD}/doc/java-code-tracer-sample-application.jar"To run over TCP, switch config file to:
-Djct.config="${PWD}/doc/config-sample-helloworld-tcp.yaml"
In Kibana:
- Go to
Stack Management -> Data Views. - Click
Create data view. - Name:
JCT Events(or any name). - Index pattern:
jct-events-*. - Time field:
- If a timestamp field is available, select it.
- Otherwise choose the option without a time filter.
- Open
Discover. - Select your data view (
jct-events-*). - Add useful columns, for example:
stacktimestampMillis
- Use KQL filters to narrow results.
Examples:
stack:*InSubA*stack:*integration.package*
Send one JSON event directly to Logstash:
echo '{"service":"jct","msg":"hello from udp","level":"INFO"}' | nc -u -w1 localhost 9999Then verify Elasticsearch has data:
curl -s "http://localhost:9200/jct-events-*/_search?pretty"Send one JSON line to Logstash over TCP:
echo '{"service":"jct","msg":"hello from tcp","level":"INFO"}' | nc -w1 localhost 9999Then verify Elasticsearch has data:
curl -s "http://localhost:9200/jct-events-*/_search?pretty"- No indices in Elasticsearch:
- Check Logstash logs:
docker compose logs --no-color logstash | tail -n 200 - Confirm UDP/TCP target is
localhost:9999in your JCT config.
- Check Logstash logs:
- Kibana shows no documents:
- Confirm data view matches
jct-events-*. - Expand the time range in Discover.
- Confirm data view matches
- Port already in use:
- Stop conflicting services or change host port mappings in
docker-compose.yml.
- Stop conflicting services or change host port mappings in
docker compose downRemove containers and named volumes (elasticsearch-data, kibana-data), then start fresh:
docker compose down -v
docker compose up -dWait ~30 seconds for health checks, then send a smoke event to recreate the jct-events-* index:
printf '{"service":"jct","msg":"smoke","stack":["com.example.Foo.bar()"]}\n' | nc -u -w1 127.0.0.1 9999Verify the index exists:
curl -s "http://localhost:9200/_cat/indices/jct-events-*?v"To also remove locally built/pulled images:
docker compose down -v --rmi local
docker compose up -d