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.
| Protocol | Node.js | Python | Java | Go | PHP |
|---|---|---|---|---|---|
| HTTP — requests inA request the service receives | Yes | Yes | Yes | Yes | Yes |
| HTTP — calls outA call the service makes | Yes | Yes | Yes | Yes | Guzzle, Symfony |
| AMQP (RabbitMQ) — publishA message put on a queue | Yes | Yes | Not yet | Yes | Not yet |
| AMQP (RabbitMQ) — consumeA message taken off one | Yes | Yes | Not yet | Delivery only | Not yet |
| SQS — delivered to a local handlerThrough a trigger drawn on the canvas | Yes | Yes | Not yet | Not yet | Not yet |
| SNS — delivered to a local handlerThrough a trigger drawn on the canvas | Yes | Yes | Not yet | Not yet | Not yet |
| Lambda-style handlerRun as an event service | Yes | Yes | Not yet | Not yet | Not 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
SendMessageorPublishanswered 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
| Feature | Node.js | Python | Java | Go | PHP |
|---|---|---|---|---|---|
| Execution recordingWhat the service's own code did | Yes | Yes, 3.12+ | Yes | Yes | Not yet |
| RedirectsA line drawn out of the service | Yes | Yes | Yes | Yes | Not yet |
| Function overrides | Yes | Yes | Not yet | Not yet | Not yet |
| Stopping a send | Yes | Yes | Not yet | Not yet | Not yet |
| Requests from outside Debugt | Yes | Yes | Yes | Yes | Yes |
| Redacted headers, Don't capture hops | Yes | Yes | Yes | Yes | Yes |
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
undiciPoolorClientis 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
awaitis 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’sconsume()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,
thisis 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 betweenhttpandhttps; 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
CONSUMEhop 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
-overlayinbuildFlagskeeps 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.phpis 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 serveandsymfony server:startstart 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_filekeeps 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:
localhostor127.0.0.1.