Yet another thing that has interested me for a while was how to approach logging in a Rust application. Until now, I was just relying on println() macro to display significant events in my torrent client. However, this is a poor-man’s solution: in production applications, you don’t want to rely on println() statements for logging: your approach to logging should be more systematic.

Usually developers rely on some sort of a logging library that offers developers flexible and configurable means to manage logs. I was unaware of such libraries in Rust, until I came across an excellent video by Jon Gjengset, Decrusting the tracing crate, that introduced me to the Tracing framework and gave me the answer I needed.

Using Tracing in my project

As described in the documentation, Tracing provides a developer with powerful capabilities to keep track of what’s going on in the application using spans and events. I can imagine how it can be useful to record useful information on several levels:

  • On the low level, we can keep track of the message exchange with the remote peer: what messages we send to the peer and what we receive from it;
  • On the higher level, we can record the process of downloading the file piece by piece. Since downloading a single piece is also done in separate blocks, it makes sense to keep track of requesting and receiving individual blocks as well.

Using Tracing, we can collect the diagnostic information throughout application layers from top to bottom, as a nice hierarchy of spans. At this time, however, I want to keep things simple. My goal is to get rid of println() statements in the code, and replace them with appropriate info() and debug() macros from Tracing.

I’m also not going to work much on consuming the events yet. There’s a plethora of implementations in Tracing ecosystem that allows you to send collected traces to OpenTelemetry, Sentry, etc. For now, however, I’m quite satisfied with the ready-to-use implementation from tracing_subscriber crate, which prints events to the console.

With Tracing in place, the console output of the application looks more like a production log:

2025-12-02T06:21:03.526813Z DEBUG reqwest::connect: starting new connection: http://bttracker.debian.org:6969/
2025-12-02T06:21:03.529348Z DEBUG hyper_util::client::legacy::connect::http: connecting to 130.239.18.158:6969
2025-12-02T06:21:03.672770Z DEBUG hyper_util::client::legacy::connect::http: connected to 130.239.18.158:6969
2025-12-02T06:21:03.757824Z  INFO main: Received peer addresses peer_count=50
2025-12-02T06:21:03.757899Z  INFO main: Probing peers
2025-12-02T06:21:03.757996Z DEBUG request_complete_file{peer_addr=194.15.111.74:53817}: bt_client: Connecting to peer
2025-12-02T06:21:08.759082Z DEBUG request_complete_file{peer_addr=194.15.111.74:53817}: bt_client: Failed to connect error=connection timed out
2025-12-02T06:21:08.759370Z DEBUG request_complete_file{peer_addr=203.12.8.206:35474}: bt_client: Connecting to peer
2025-12-02T06:21:09.403123Z DEBUG request_complete_file{peer_addr=203.12.8.206:35474}: bt_client: Failed to connect error=failed to fill whole buffer
2025-12-02T06:21:09.403369Z DEBUG request_complete_file{peer_addr=86.248.46.31:6881}: bt_client: Connecting to peer
2025-12-02T06:21:09.917451Z DEBUG request_complete_file{peer_addr=86.248.46.31:6881}: bt_client: Connected remote_id=-TR2930-0hc2sqt9mid4
2025-12-02T06:21:09.917536Z DEBUG request_complete_file{peer_addr=86.248.46.31:6881}: bt_client: Connected, requesting file
2025-12-02T06:21:16.865306Z DEBUG request_complete_file{peer_addr=86.248.46.31:6881}: bt_client: Ready to download
2025-12-02T06:21:16.865451Z  INFO main: Downloading file file_size=702545920 piece_count=2680 peer_address=86.248.46.31:6881 remote_id=-TR2930-0hc2sqt9mid4
2025-12-02T06:21:17.899175Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=0 duration_ms=1032
2025-12-02T06:21:17.971705Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=1 duration_ms=72
2025-12-02T06:21:18.542541Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=2 duration_ms=570
2025-12-02T06:21:18.576846Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=3 duration_ms=34
2025-12-02T06:21:18.585826Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=4 duration_ms=8
2025-12-02T06:21:18.622275Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=5 duration_ms=36
2025-12-02T06:21:18.630372Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=6 duration_ms=8
2025-12-02T06:21:18.901433Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=7 duration_ms=271
2025-12-02T06:21:18.914199Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=8 duration_ms=12
2025-12-02T06:21:18.924021Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=9 duration_ms=9
2025-12-02T06:21:18.932650Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=10 duration_ms=8

......< skipped rest of events >........

2025-12-02T06:23:41.616984Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=2676 duration_ms=6
2025-12-02T06:23:41.623190Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=2677 duration_ms=6
2025-12-02T06:23:41.629119Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=2678 duration_ms=5
2025-12-02T06:23:42.082875Z DEBUG bt_client::downloader::file_downloader: Downloaded piece piece_index=2679 duration_ms=453
2025-12-02T06:23:42.082961Z  INFO main: Received entire file file_bytes="455208000000909000000000000000000000000000000000000000000000000033edfa8ed5bc007cfbfc6631db6631c96653665106578edd8ec552be007cbf0006b90001f3a5ea4b06000052b441bbaa5531c930f6f9cd13721681fb55aa751083e101740b66c706f306b442eb15eb0231c95a51b408cd135b0fb6c6405083e1" file_size=702545920 download_duration="145.22s"

It’s quite verbose, but at the same time that’s the kind of log file I’d expect to see from a production application.

We’re still not seeing the full picture in the log, though. In the end, I’d like to be able to see the detailed information about TCP communication: what messages get passed over the TCP connection in both directions. I’ll leave it to the future, though: as I extend the functionality of the client, I’ll add more tracing to the relevant parts in the code.

Next steps

Working on logging got me anxious about introducing some sort of a user interface to the application. It would be nice to see the download progress in a prettier way, not just as the log entries!