chore(logging): add detailed debug logging to instance start and tunnel creation
Add comprehensive logging to trace 500 error: - Log each step of docker compose up (returncode, stdout, stderr) - Log container ID and name after start - Log tool type and port being used - Log each step of Cloudflare tunnel creation with API responses - Log cloudflared config updates This will help identify exactly where the failure occurs.
This commit is contained in:
@@ -345,6 +345,7 @@ async def start_instance(
|
|||||||
|
|
||||||
instance.status = "building"
|
instance.status = "building"
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
logger.info("Starting instance %s (name=%s)", instance.id, instance.name)
|
||||||
|
|
||||||
# Fetch tool configs for this tool type
|
# Fetch tool configs for this tool type
|
||||||
env_vars = {}
|
env_vars = {}
|
||||||
@@ -359,6 +360,7 @@ async def start_instance(
|
|||||||
|
|
||||||
config_result = await session.execute(config_query)
|
config_result = await session.execute(config_query)
|
||||||
configs = config_result.scalars().all()
|
configs = config_result.scalars().all()
|
||||||
|
logger.info("Found %d tool configs for instance %s", len(configs), instance.id)
|
||||||
|
|
||||||
for config in configs:
|
for config in configs:
|
||||||
if config.config_type == "env":
|
if config.config_type == "env":
|
||||||
@@ -372,18 +374,24 @@ async def start_instance(
|
|||||||
|
|
||||||
if env_vars:
|
if env_vars:
|
||||||
env_file_path = write_env_file(instance_dir, env_vars)
|
env_file_path = write_env_file(instance_dir, env_vars)
|
||||||
|
logger.info("Wrote env file for instance %s: %s", instance.id, env_file_path)
|
||||||
|
|
||||||
if config_files:
|
if config_files:
|
||||||
write_config_files(instance_dir, config_files)
|
write_config_files(instance_dir, config_files)
|
||||||
|
logger.info("Wrote %d config files for instance %s", len(config_files), instance.id)
|
||||||
|
|
||||||
# Execute docker compose up with env file
|
# Execute docker compose up with env file
|
||||||
|
logger.info("Running docker compose up for instance %s (compose_path=%s)", instance.id, instance.compose_path)
|
||||||
returncode, stdout, stderr = execute_compose_command(
|
returncode, stdout, stderr = execute_compose_command(
|
||||||
instance.compose_path, "up", env_file=env_file_path
|
instance.compose_path, "up", env_file=env_file_path
|
||||||
)
|
)
|
||||||
|
logger.info("Docker compose up completed for instance %s: returncode=%d, stdout=%s, stderr=%s",
|
||||||
|
instance.id, returncode, stdout[:200] if stdout else "", stderr[:500] if stderr else "")
|
||||||
|
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
instance.status = "error"
|
instance.status = "error"
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
logger.error("Failed to start instance %s: %s", instance.id, stderr)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
detail=f"failed to start instance: {stderr}",
|
detail=f"failed to start instance: {stderr}",
|
||||||
@@ -393,21 +401,28 @@ async def start_instance(
|
|||||||
container_id = get_container_id(instance.name)
|
container_id = get_container_id(instance.name)
|
||||||
if container_id:
|
if container_id:
|
||||||
instance.container_id = container_id
|
instance.container_id = container_id
|
||||||
|
logger.info("Container ID for instance %s: %s", instance.id, container_id)
|
||||||
|
|
||||||
container_name = get_container_name(instance.name)
|
container_name = get_container_name(instance.name)
|
||||||
if container_name:
|
if container_name:
|
||||||
instance.container_name = container_name
|
instance.container_name = container_name
|
||||||
|
logger.info("Container name for instance %s: %s", instance.id, container_name)
|
||||||
|
|
||||||
instance.status = "running"
|
instance.status = "running"
|
||||||
instance.last_started_at = datetime.now()
|
instance.last_started_at = datetime.now()
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
logger.info("Instance %s is now running", instance.id)
|
||||||
|
|
||||||
# Get tool type for default port
|
# Get tool type for default port
|
||||||
tool_type = await session.get(ToolType, instance.tool_type_id)
|
tool_type = await session.get(ToolType, instance.tool_type_id)
|
||||||
instance_port = tool_type.default_port if tool_type and tool_type.default_port else 8080
|
instance_port = tool_type.default_port if tool_type and tool_type.default_port else 8080
|
||||||
|
logger.info("Tool type for instance %s: name=%s, default_port=%s",
|
||||||
|
instance.id, tool_type.name if tool_type else "unknown", instance_port)
|
||||||
|
|
||||||
# Create Cloudflare tunnel for public access
|
# Create Cloudflare tunnel for public access
|
||||||
try:
|
try:
|
||||||
|
logger.info("Creating Cloudflare tunnel for instance %s (name=%s, port=%d)",
|
||||||
|
instance.id, instance.name, instance_port)
|
||||||
tunnel_info = await create_tunnel(
|
tunnel_info = await create_tunnel(
|
||||||
instance_name=instance.name,
|
instance_name=instance.name,
|
||||||
instance_id=str(instance.id),
|
instance_id=str(instance.id),
|
||||||
@@ -418,12 +433,13 @@ async def start_instance(
|
|||||||
instance.url = tunnel_info["public_url"]
|
instance.url = tunnel_info["public_url"]
|
||||||
await session.commit()
|
await session.commit()
|
||||||
logger.info(
|
logger.info(
|
||||||
"Created tunnel for instance %s: %s",
|
"Created tunnel for instance %s: tunnel_id=%s, url=%s",
|
||||||
instance.id,
|
instance.id,
|
||||||
|
tunnel_info["tunnel_id"],
|
||||||
tunnel_info["public_url"],
|
tunnel_info["public_url"],
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning(
|
logger.exception(
|
||||||
"Failed to create tunnel for instance %s: %s. Falling back to proxy URL.",
|
"Failed to create tunnel for instance %s: %s. Falling back to proxy URL.",
|
||||||
instance.id,
|
instance.id,
|
||||||
exc,
|
exc,
|
||||||
|
|||||||
@@ -45,11 +45,17 @@ async def create_tunnel(
|
|||||||
if not settings.cloudflare_api_token:
|
if not settings.cloudflare_api_token:
|
||||||
raise ValueError("CLOUDFLARE_API_TOKEN not configured")
|
raise ValueError("CLOUDFLARE_API_TOKEN not configured")
|
||||||
|
|
||||||
|
logger.info("Creating Cloudflare tunnel for instance_name=%s, instance_id=%s, port=%d",
|
||||||
|
instance_name, instance_id, instance_port)
|
||||||
|
logger.info("Cloudflare config: account_id=%s, zone_id=%s, base_domain=%s",
|
||||||
|
settings.cloudflare_account_id, settings.cloudflare_zone_id, settings.cloudflare_base_domain)
|
||||||
|
|
||||||
headers = _get_headers(settings)
|
headers = _get_headers(settings)
|
||||||
account_id = settings.cloudflare_account_id
|
account_id = settings.cloudflare_account_id
|
||||||
|
|
||||||
# Create tunnel
|
# Create tunnel
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
|
logger.info("Step 1: Creating tunnel via Cloudflare API...")
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
f"{CLOUDFLARE_API_BASE}/accounts/{account_id}/cfd_tunnel",
|
f"{CLOUDFLARE_API_BASE}/accounts/{account_id}/cfd_tunnel",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
@@ -58,6 +64,7 @@ async def create_tunnel(
|
|||||||
"config_src": "cloudflare",
|
"config_src": "cloudflare",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
logger.info("Tunnel creation response: status=%d, body=%s", response.status_code, response.text[:500])
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
@@ -66,19 +73,24 @@ async def create_tunnel(
|
|||||||
|
|
||||||
tunnel = data["result"]
|
tunnel = data["result"]
|
||||||
tunnel_id = tunnel["id"]
|
tunnel_id = tunnel["id"]
|
||||||
|
logger.info("Step 1 complete: tunnel_id=%s", tunnel_id)
|
||||||
|
|
||||||
# Get tunnel token
|
# Get tunnel token
|
||||||
|
logger.info("Step 2: Getting tunnel token...")
|
||||||
token_response = await client.get(
|
token_response = await client.get(
|
||||||
f"{CLOUDFLARE_API_BASE}/accounts/{account_id}/cfd_tunnel/{tunnel_id}/token",
|
f"{CLOUDFLARE_API_BASE}/accounts/{account_id}/cfd_tunnel/{tunnel_id}/token",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
|
logger.info("Token response: status=%d", token_response.status_code)
|
||||||
token_response.raise_for_status()
|
token_response.raise_for_status()
|
||||||
token_data = token_response.json()
|
token_data = token_response.json()
|
||||||
tunnel_token = token_data["result"]
|
tunnel_token = token_data["result"]
|
||||||
|
logger.info("Step 2 complete: got tunnel token")
|
||||||
|
|
||||||
# Create DNS record for the tunnel
|
# Create DNS record for the tunnel
|
||||||
subdomain = f"instance-{instance_id[:8]}"
|
subdomain = f"instance-{instance_id[:8]}"
|
||||||
hostname = f"{subdomain}.{settings.cloudflare_base_domain}"
|
hostname = f"{subdomain}.{settings.cloudflare_base_domain}"
|
||||||
|
logger.info("Step 3: Creating DNS record for subdomain=%s, hostname=%s", subdomain, hostname)
|
||||||
|
|
||||||
dns_response = await client.post(
|
dns_response = await client.post(
|
||||||
f"{CLOUDFLARE_API_BASE}/zones/{settings.cloudflare_zone_id}/dns_records",
|
f"{CLOUDFLARE_API_BASE}/zones/{settings.cloudflare_zone_id}/dns_records",
|
||||||
@@ -91,9 +103,12 @@ async def create_tunnel(
|
|||||||
"proxied": True,
|
"proxied": True,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
logger.info("DNS response: status=%d, body=%s", dns_response.status_code, dns_response.text[:500])
|
||||||
dns_response.raise_for_status()
|
dns_response.raise_for_status()
|
||||||
|
logger.info("Step 3 complete: DNS record created")
|
||||||
|
|
||||||
# Update cloudflared config
|
# Update cloudflared config
|
||||||
|
logger.info("Step 4: Updating cloudflared config...")
|
||||||
await update_cloudflared_config(
|
await update_cloudflared_config(
|
||||||
tunnel_id=tunnel_id,
|
tunnel_id=tunnel_id,
|
||||||
tunnel_token=tunnel_token,
|
tunnel_token=tunnel_token,
|
||||||
@@ -102,7 +117,9 @@ async def create_tunnel(
|
|||||||
instance_port=instance_port,
|
instance_port=instance_port,
|
||||||
settings=settings,
|
settings=settings,
|
||||||
)
|
)
|
||||||
|
logger.info("Step 4 complete: cloudflared config updated")
|
||||||
|
|
||||||
|
logger.info("Tunnel creation complete: tunnel_id=%s, public_url=https://%s", tunnel_id, hostname)
|
||||||
return {
|
return {
|
||||||
"tunnel_id": tunnel_id,
|
"tunnel_id": tunnel_id,
|
||||||
"public_url": f"https://{hostname}",
|
"public_url": f"https://{hostname}",
|
||||||
|
|||||||
Reference in New Issue
Block a user