A proxy server for intercepting and logging data sent by WhaTap Mobile SDKs (Android/iOS). This tool is designed for development and testing purposes to monitor and debug SDK data transmission.
- π Complete Request/Response Logging - Capture all HTTP traffic from WhaTap SDKs
- π Transparent Proxy - Forward requests to the actual WhaTap server
- π Structured Logging - Organized logs with Winston logger
- π Request Tracking - Unique request IDs for easy debugging
- π Web UI - Browse logs through web interface
- π Real-time Monitoring - Live request/response tracking
- π± Multi-platform Support - Works with both Android and iOS SDKs
- Node.js v14 or higher
- npm or yarn
# Clone the repository
git clone https://github.com/whatap/whatap-mobile-proxy-server.git
cd whatap-mobile-proxy-server
# Install dependencies
npm install
# Copy environment file
cp .env.example .env
# Start the server
npm start
# Run with auto-restart on file changes
npm run dev
Create a .env
file in the root directory:
# Server Configuration
PORT=8080
TARGET_SERVER=https://rumote.whatap-mobile-agent.io
# Logging
LOG_LEVEL=info
LOG_DIR=./logs
# Optional: Custom headers
CUSTOM_HEADERS={"X-Proxy-Version": "1.0"}
// Kotlin
WhatapAgent.Builder.newBuilder()
.setProjectKey("YOUR-PROJECT-KEY")
.setServerUrl("http://YOUR-IP:8080") // Proxy server URL
.setSampling(1.0)
.build(application)
// Java
WhatapAgent.Builder.newBuilder()
.setProjectKey("YOUR-PROJECT-KEY")
.setServerUrl("http://YOUR-IP:8080") // Proxy server URL
.setSampling(1.0)
.build(getApplication());
// Swift
WhaTapAgent.shared.config.serverUrl = "http://YOUR-IP:8080"
WhaTapAgent.shared.config.projectKey = "YOUR-PROJECT-KEY"
WhaTapAgent.shared.start()
// Objective-C
[[WhaTapAgent shared] configWithServerUrl:@"http://YOUR-IP:8080"];
[[WhaTapAgent shared] configWithProjectKey:@"YOUR-PROJECT-KEY"];
[[WhaTapAgent shared] start];
Use your machine's IP address:
# Find your IP
ifconfig | grep "inet " | grep -v 127.0.0.1
# Use localhost with ADB reverse
adb reverse tcp:8080 tcp:8080
# Then use: http://localhost:8080
# Use localhost directly
http://localhost:8080
# View live logs in terminal
npm start
Daily rotating log files are saved in the logs/
directory:
logs/
βββ proxy-2025-07-26.log
βββ proxy-2025-07-27.log
βββ ...
Access the web interface for browsing logs:
- Log Index: http://localhost:8080/proxy-logs
- Specific Log: http://localhost:8080/proxy-logs/proxy-YYYY-MM-DD.log
{
"requestId": "req-1753538121415-abc123",
"method": "POST",
"path": "/log",
"headers": {
"content-type": "application/json",
"user-agent": "whatap-android-agent/1.0.2"
},
"body": {
"logs": [...],
"meta": {
"agent_version": "1.0.2",
"device_name": "Samsung Galaxy S21",
"project_access_key": "YOUR-PROJECT-KEY"
}
},
"targetUrl": "https://rumote.whatap-mobile-agent.io/log",
"statusCode": 200,
"duration": "45ms",
"timestamp": "2025-07-26T13:55:21.415Z"
}
All requests to the proxy server are forwarded to the target WhaTap server:
Description: Handles user-generated logs and custom events
Expected Data Structure:
{
"resource_logs": [{
"scope_logs": [{
"log_records": [{
"trace_id": null,
"span_id": null,
"severity_text": "INFO",
"severity_number": 9,
"created_at": 1753600823493,
"body": "userLog",
"is_user_log": true,
"attributes": [
{"key": "event_name", "value": "userLog"},
{"key": "messages", "value": "User custom message"}
]
}],
"scope": {
"name": "userlog",
"attributes": [],
"version": null
}
}]
}],
"meta": {
"agent_version": "1.0.2",
"device_name": "vivo V2041",
"p_code": 12345,
"app_version": "1.0",
"user_id": "f1cebb51-b281-4a00-96ff-34600d0f10eb",
"project_access_key": "DEMO-PROJECT-KEY-001",
"os_version": "13",
"session_id": "989a57fb-059a-4f75-a3c9-3ba8ec9e8966",
"server_url": "http://localhost:8080/trace"
}
}
Trigger Events:
UserLogger.print("message")
callsUserLogger.print(mapData)
calls- Crash detection
- Custom event logging
Description: Handles Activity/Fragment lifecycle and network request traces
Expected Data Structure:
{
"resource_spans": [{
"scope_spans": [{
"spans": [{
"name": "Created",
"trace_id": "26bab000e7bc4619bffcea6cce8b2b9c",
"span_id": "4efc59774dc44046",
"start_time_unix_milli": 1753600823493,
"end_time_unix_milli": 1753600823496,
"attributes": [
{"key": "activity_name", "value": "NetworkTestActivity"},
{"key": "screen_name", "value": "NetworkTestActivity"},
{"key": "start_type", "value": "normal|cold|warm|hot"}
],
"events": [
{"name": "activityPreCreated", "timestamp_unix_milli": 1753600823493},
{"name": "activityCreated", "timestamp_unix_milli": 1753600823494}
],
"metrics": {
"cpu": [26, 0, 0, 0, 0, 0],
"memory_dalvic": [1165, 1165, 1165, 1165, 1165, 1165],
"memory_native": [3723, 3723, 3723, 3723, 3723, 3723],
"memory_other": [30587, 30587, 30587, 30587, 30587, 30587],
"battery_power_save": 0
}
}]
}]
}]
}
Trigger Events:
- Activity onCreate(), onStart(), onResume()
- Fragment lifecycle events
- Network requests (OkHttp, Retrofit, Volley, HttpURLConnection)
- ANR detection (5+ second UI blocking)
Volley Requests:
{
"name": "GET",
"attributes": [
{"key": "network_library", "value": "Volley"},
{"key": "full_url", "value": "https://api.example.com/data"},
{"key": "status_code", "value": "200"}
]
}
OkHttp Interceptor:
{
"name": "POST",
"attributes": [
{"key": "http_method", "value": "POST"},
{"key": "screen_name", "value": "NetworkTestActivity"}
]
}
OkHttp EventListener:
{
"name": "HTTP GET jsonplaceholder.typicode.com",
"attributes": [
{"key": "http.status_code", "value": "200"}
]
}
HttpURLConnection:
{
"name": "GET",
"attributes": [
{"key": "network_library", "value": "HttpUrlConnection"}
]
}
GET /health
- Health checkGET /proxy-logs
- List log filesGET /proxy-logs/:filename
- View specific log file
# Test the proxy server
curl -X GET http://localhost:8080/health
# Expected Response
{"status": "ok", "timestamp": "2025-07-27T12:00:00.000Z"}
curl -X POST http://localhost:8080/log \
-H "Content-Type: application/json" \
-H "User-Agent: whatap-android-agent/1.0.2" \
-d '{
"resource_logs": [{
"scope_logs": [{
"log_records": [{
"body": "userLog",
"is_user_log": true,
"created_at": '$(date +%s%3N)',
"attributes": [
{"key": "event_name", "value": "userLog"},
{"key": "messages", "value": "Test message from curl"}
]
}],
"scope": {"name": "userlog"}
}]
}],
"meta": {
"agent_version": "1.0.2",
"device_name": "Test Device",
"project_access_key": "TEST-KEY",
"session_id": "test-session-123"
}
}'
curl -X POST http://localhost:8080/log \
-H "Content-Type: application/json" \
-d '{
"resource_logs": [{
"scope_logs": [{
"log_records": [{
"body": "userLog",
"attributes": [
{"key": "event_name", "value": "userLog"},
{"key": "test_type", "value": "curl_test"},
{"key": "user_id", "value": "test_user_001"},
{"key": "timestamp", "value": "'$(date +%s%3N)'"}
]
}],
"scope": {"name": "userlog"}
}]
}],
"meta": {
"project_access_key": "TEST-KEY",
"device_name": "curl-test-device"
}
}'
curl -X POST http://localhost:8080/trace \
-H "Content-Type: application/json" \
-d '{
"resource_spans": [{
"scope_spans": [{
"spans": [{
"name": "Created",
"trace_id": "26bab000e7bc4619bffcea6cce8b2b9c",
"span_id": "4efc59774dc44046",
"start_time_unix_milli": '$(date +%s%3N)',
"end_time_unix_milli": '$(($(date +%s%3N) + 100))',
"attributes": [
{"key": "activity_name", "value": "TestActivity"},
{"key": "start_type", "value": "normal"}
],
"events": [
{"name": "activityCreated", "timestamp_unix_milli": '$(date +%s%3N)'}
]
}]
}]
}]
}'
curl -X POST http://localhost:8080/trace \
-H "Content-Type: application/json" \
-d '{
"resource_spans": [{
"scope_spans": [{
"spans": [{
"name": "GET",
"trace_id": "abc123def456",
"span_id": "xyz789",
"attributes": [
{"key": "network_library", "value": "OkHttp"},
{"key": "http_method", "value": "GET"},
{"key": "url", "value": "https://api.example.com/test"}
]
}]
}]
}]
}'
# 1. Start proxy server
npm start
# 2. Setup ADB reverse (for Android emulator/device)
adb reverse tcp:8080 tcp:8080
# 3. Install sample app
cd ../
adb install -r app/build/outputs/apk/debug/app-debug.apk
Scenario 1: Basic UserLogger Test
- Launch CalmMind app
- Navigate to "π§ͺ Network Test Suite"
- Click "π Test Span vs LogRecord Types"
- Check proxy logs for
/log
requests
Scenario 2: Network Library Testing
- Click "π Test Library Differences"
- Verify different network library patterns in logs
- Look for
network_library
attributes in requests
Scenario 3: Performance Metrics
- Click "π Test Metrics Collection (10s cycle)"
- Wait 10-15 seconds
- Check for metrics arrays with 6 elements each
Scenario 4: ANR Testing
- Click "Trigger ANR (5s sleep)" button
- Wait for UI to unfreeze
- Check logs for ANR span data
# Monitor live logs
tail -f logs/proxy-$(date +%Y-%m-%d).log | grep -E "(POST|GET)"
# Filter UserLogger data
tail -f logs/proxy-$(date +%Y-%m-%d).log | grep -A 20 '"path": "/log"'
# Filter Activity traces
tail -f logs/proxy-$(date +%Y-%m-%d).log | grep -A 20 '"path": "/trace"'
# Use the provided analysis script
cd ../
./analyze-data-types.sh
{
"requestId": "req-1753600823493-abc123",
"statusCode": 200,
"duration": "45ms",
"message": "π€ Outgoing Response"
}
{
"requestId": "req-1753600823493-def456",
"statusCode": 404,
"data": "404 page not found",
"message": "π€ Outgoing Response"
}
For successful testing, verify:
- β
UserLogger Data:
/log
endpoint receives requests withname: "userlog"
- β
Activity Traces:
/trace
endpoint receives spans withname: "Created"
- β Network Requests: Library-specific attributes in span data
- β Metrics Arrays: CPU and memory arrays have exactly 6 elements
- β Session Tracking: Consistent session_id across requests
- β Device Info: Proper device_name and os_version in meta
# Find process using port 8080
lsof -i :8080
# Kill the process
kill -9 <PID>
# Fix permissions
chmod -R 755 logs/
- Check firewall settings
- Ensure the server is running
- Verify the correct IP address
- The proxy adds minimal latency (typically < 5ms)
- Log files are rotated daily to prevent disk space issues
- Request/response bodies are limited to prevent memory issues
- Async logging to minimize performance impact
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- WhaTap APM for the monitoring platform
- Express.js for the web framework
- Winston for logging
- All contributors and testers
Note: This proxy server is intended for development and testing purposes only. Do not use in production environments.