Documentation

Language support and limits

A tracer goes into each service as Debugt starts it. What it can see depends on the language, the protocol, and the library the service uses.

Protocols

What Debugt follows from one service into the next, by the language of the service doing it.

ProtocolNode.jsPythonJavaGoPHP
HTTP — requests inA request the service receivesYesYesYesYesYes
HTTP — calls outA call the service makesYesYesYesYesGuzzle, Symfony
AMQP (RabbitMQ) — publishA message put on a queueYesYesNot yetYesNot yet
AMQP (RabbitMQ) — consumeA message taken off oneYesYesNot yetDelivery onlyNot yet
SQS — delivered to a local handlerThrough a trigger drawn on the canvasYesYesNot yetNot yetNot yet
SNS — delivered to a local handlerThrough a trigger drawn on the canvasYesYesNot yetNot yetNot yet
Lambda-style handlerRun as an event serviceYesYesNot yetNot yetNot yet
  • HTTP is the base: every language has requests in, and calls out through the libraries listed below.
  • AMQP means RabbitMQ, through the client library the service already uses. Nothing is configured — no broker address, no exchange, no queue.
  • SQS and SNS are not brokers Debugt speaks to. A trigger drawn on the canvas has a service’s SendMessage or Publish answered locally and handed to an event service’s handler. Without a trigger, an SQS or SNS call is an ordinary HTTP call to AWS, and shows as a hop in every language that traces calls out.
  • Other protocols — gRPC, Kafka, MQTT, WebSockets, database drivers — are not followed. A service that uses them still runs and has its HTTP traced.

Features

FeatureNode.jsPythonJavaGoPHP
Execution recordingWhat the service's own code didYesYes, 3.12+YesYesNot yet
RedirectsA line drawn out of the serviceYesYesYesYesNot yet
Function overridesYesYesNot yetNot yetNot yet
Stopping a sendYesYesNot yetNot yetNot yet
Requests from outside DebugtYesYesYesYesYes
Redacted headers, Don't capture hopsYesYesYesYesYes

Libraries covered

Requests in Calls out RabbitMQ
Node.js Node’s own http server, and so Express, Fastify, Koa and Nest core http/https (axios, node-fetch, got, the AWS SDK), global fetch, undici amqplib
Python ASGI (FastAPI, Starlette, Django), WSGI (Flask, Django), the standard library’s http.server http.client — requests, urllib3, urllib, boto3 pika
Java The servlet API, javax or jakarta (Tomcat, Jetty, Undertow, Spring MVC), and com.sun.net.httpserver HttpURLConnection, java.net.http.HttpClient, Apache HttpClient 4 —
Go net/http — the standard mux, chi, gin, echo, gorilla net/http’s client rabbitmq/amqp091-go, streadway/amqp
PHP Any script or the built-in server Debugt starts Guzzle 6 and 7, Symfony HttpClient —

A hop joins the tree of the request that caused it through a traceparent header, passed from one service to the next. A service in a language or with a library that is not covered simply does not pass it on: the tree stops there, and nothing breaks.

Node.js

Launch type node. The tracer is loaded with --require through NODE_OPTIONS, so it also reaches a process started by a package script.

Limits

  • An undici Pool or Client is not redirected: it was built against an address the code already chose.
  • A RabbitMQ consumer that starts work and does not return its promise has its delivery end there. What it goes on to call is still drawn under the delivery, but what its code did after the first await is not in the recording.
  • Messages fetched with channel.get, rather than delivered to a consumer, are not hops.
  • Other AMQP clients, such as rhea, are not covered.
  • A function run away from its module — one handed to Puppeteer’s page.evaluate, or a worker built from a function’s text — runs as written, and is not recorded.

Python

Launch type debugpy, or the older python. The tracer is found through PYTHONPATH; nothing is installed into your environment.

Limits

  • Execution recording needs Python 3.12 or later. Hops are traced on earlier versions.
  • httpx and aiohttp calls are not traced. They bring their own networking and do not go through http.client. A call made with one is not a hop, is not redirected, and passes no trace on.
  • aioboto3 is not covered for SQS and SNS triggers, for the same reason. boto3 is.
  • Messages fetched with basic_get, or read from pika’s consume() generator, are not hops.
  • Other AMQP clients — aio-pika, kombu — are not covered.

Java, Kotlin, Scala

Launch type java. One agent jar is added to the JVM’s vmArgs.

Execution covers classes loaded from a folder (target/classes, bin/main) and compiled with debug information, which is the default for VS Code and Maven. Values are described from their fields and never by calling toString.

Limits

  • Servers that are not servlets are not seen: WebFlux on Netty, Vert.x.
  • OkHttp and Apache HttpClient 5 calls are not traced.
  • Other threads. A call made from a thread other than the one that took the request is neither traced nor recorded.
  • No messages. RabbitMQ publishes and deliveries are not hops, and SQS and SNS triggers are not delivered from a Java service.
  • No function overrides, and no stopping a send.
  • In a recording, this is not shown, changes more than one level inside an object are not followed, and repeated lines are not folded.
  • With HttpURLConnection, a redirect for one method cannot move a call between http and https; a redirect for every method can.

Go

Launch type go, with "mode": "debug", "auto" or "test". Go has nothing to load a tracer into, so the service is built with it: an overlay adds a file to net/http for the build, without touching your installed Go or your source.

Limits

  • Only what the debugger builds. A binary built beforehand ("mode": "exec"), an attached process and a container were built without the tracer.
  • Only net/http. fasthttp (and so Fiber), gRPC, and clients with their own HTTP/2 transport are not seen.
  • Pass the request’s context. A call made with http.NewRequestWithContext(r.Context(), …) is found under its request on any goroutine. A call on another goroutine without that context belongs to no request, and is not a hop.
  • Execution covers the handler’s goroutine only. Work handed to another goroutine is not recorded, nor is a handler under http.TimeoutHandler, or a service started in "mode": "test".
  • A consumer’s work is not under its delivery. A Go consumer is a loop reading a channel, so there is no telling where the handling of one message ends. The CONSUME hop runs until the message is acknowledged, and the trace ends there: the calls the consumer then makes are not hops.
  • No function overrides, SQS or SNS triggers, or stopping a send.
  • Generated files, cgo and test files are built as they are and leave no steps. Max repeats per line does nothing for Go.
  • The first build is slower, by a few seconds, once per Go version.
  • One overlay. A configuration with its own -overlay in buildFlags keeps it, and is not traced. A service in a Go workspace (go.work) is traced over HTTP but not for messages.

PHP

Launch type php. PHP is traced less completely than the others, for one reason: PHP code cannot stand in for the functions PHP itself provides, such as curl_exec. So a PHP service’s calls are seen through the client library that makes them, where that library can be reached.

A hop Trace passed on Bodies
The request into the service Yes — Request and response
A call with Guzzle 6 or 7 Yes Yes Request and response
A call with Symfony HttpClient, from HttpClient::create() Yes Yes Request only
file_get_contents, fopen, copy on an http:// address No Yes —
curl directly, or any other client No No —

Laravel’s Http facade and the AWS SDK for PHP are Guzzle underneath, so they are covered.

Limits

  • No execution recording. A PHP hop has no Execution, and the Record execution switch means nothing to it.
  • Calls with curl are invisible, as is any library built on it other than Guzzle and Symfony’s — Stripe’s SDK, for one.
  • No redirects, overrides or triggers out of a PHP service. Lines drawn into one work, since the caller applies those.
  • No messages, and no stopping a send.
  • Composer is needed for Guzzle and Symfony calls to be seen: vendor/autoload.php is looked for upward from where the service runs.
  • Only the PHP that Debugt starts. Start the built-in server directly — php -S localhost:8000 -t public public/index.php. php artisan serve and symfony server:start start another PHP process, and Apache, nginx with PHP-FPM or a container are started by something else; none of those is traced.
  • One prepended file. A configuration that sets its own auto_prepend_file keeps it, and is not traced.
  • A file upload (multipart/form-data) is recorded without its body.
  • Works on PHP 7.2 and up, and needs no extension.

Messages on a queue

A publish to RabbitMQ by a traced service is a hop — PUBLISH exchange/routing-key — and the delivery of that message to a consumer Debugt started is a hop under it, CONSUME queue, with what the consumer then did beneath that.

A traceparent is added to a copy of the message’s headers; a consumer that is not traced sees one header more and nothing else.

Limits, in every language

  • A consumer Debugt did not start takes the message and reports nothing. Two consumers on one queue take turns: a trace shows the delivery only when the traced one got it.
  • A message from elsewhere — published by a service Debugt did not start, or from the broker’s own console — is delivered untraced, unless Record requests from outside Debugt is on.
  • No stand-in broker. Messages go to the broker the service connected to, which has to be running.

Limits that apply everywhere

  • Execution is your own code, not your dependencies. Code under node_modules, site-packages, a jar or a Go dependency is not recorded: a recording shows your call into a library and what came back, not the steps inside it. Calls a library makes over the network are still hops. See Your code, not your dependencies.
  • Only services Debugt started are traced. One started with F5, or running in a container, has no tracer in it.
  • Bodies are kept up to 256 KB a hop; past that the rest is not captured.
  • A recording stops at its limits — steps and size, set in Settings → Recording — and says where it stopped.
  • A value is described only so far — as deep as Value depth and as long as Value items, set in the same place. Past that, a part is only named and the rest of a list is counted. See Reading a value.
  • Requests from the runner go to this machine only: localhost or 127.0.0.1.