Main Takeaways
Rust with Polars delivers predictable performance, avoiding Python's unpredictable memory spikes.
OpenTelemetry provides standardized observability with minimal performance overhead.
Data pipelines need different metrics than web apps: stage timing, throughput, and memory usage.
Feature flags enable separate CLI builds for local testing while keeping production deployments lightweight.
Structured logging with contextual attributes enables rapid debugging and performance analysis.
TL;DR: Data pipelines often fail silently, corrupting data before anyone notices. We show you how to build a high-performance, observable ETL pipeline in Rust using Polars for speed, OpenTelemetry for detailed metrics and traces, and Shuttle for zero-config deployment and telemetry export. This guide provides commented code, deployment steps, and dashboard examples to help you catch failures early, pinpoint bottlenecks, and ensure data quality from start to finish.
ETL and data engineering workflows break silently in production. A CSV file processes millions of records, but somewhere in the pipeline, wrong values slip through data validation, memory usage spikes during aggregation stages, or processing slows to a crawl without clear indicators of where the bottleneck occurs. By the time data engineers notice the issue, corrupted data has already propagated downstream.
Python developers working with Pandas frequently encounter this challenge. Memory blowups happen during large dataset processing, errors cascade across transformation stages with minimal context, and debugging performance issues requires manually adding print statements throughout the code. Traditional application monitoring tools focus on web request metrics such as latency, throughput, and error rates, but data pipelines need visibility into processing stages, memory consumption patterns, and data quality indicators.
Here's the alternative: Rust applications with Polars deliver predictable performance, OpenTelemetry provides standardized observability, and Shuttle eliminates infrastructure complexity. By implementing such a combination, you get clean, fast, observable data pipelines that surface issues before they corrupt your data.
This article explains building a production-ready data pipeline that processes real-world datasets while maintaining complete visibility into every processing stage, memory usage pattern, and performance characteristic.
Benchmark Pipeline: CSV Processing with Polars
Let me show you a working ETL example that processes NYC taxi CSV files. This pipeline demonstrates the observability challenges that make data engineering workflows different from typical web applications.
The data pipeline follows these stages: load massive CSV files (1.9GB with 12.7 million records), parse datetime fields and geographic coordinates, filter invalid records and outliers, aggregate trip patterns by time periods, and write results for downstream analysis.
Each stage presents monitoring challenges. Loading CSV files requires tracking memory allocation patterns as data enters the system. Parsing operations can fail silently when date formats don't match expectations. Filtering stages need visibility into how many records get rejected and why. Aggregation operations consume the most memory and require peak usage tracking. Output operations need validation that results match expected patterns.
Polars provides the performance foundation for this pipeline. Its lazy evaluation approach minimizes memory pressure, columnar data processing delivers consistent performance across large datasets, and structured operations make precise measurement possible. Unlike Pandas operations that can consume unpredictable amounts of memory, Polars operations have measurable resource requirements that can be monitored and alerted on.
The NYC taxi dataset contains all the characteristics that make observability critical: high record volume, data quality issues with missing coordinates and invalid timestamps, complex geographic and temporal transformations, and memory-intensive aggregation operations. Processing this dataset demonstrates how proper instrumentation reveals bottlenecks and data quality issues before they impact production systems.

What to Monitor in Data Pipelines (and Why)
Data pipeline monitoring differs fundamentally from web application observability. Instead of request latency and error rates, you need visibility into processing characteristics that determine pipeline reliability and data quality.
-
Stage-level execution time reveals processing bottlenecks. While web applications measure individual request duration, data pipelines need timing for load operations, parsing stages, transformation steps, aggregation phases, and output generation. A single slow stage can bottleneck the entire pipeline.
-
Throughput and record counts provide data volume insights. Monitor records processed per second, total record counts per stage, and record rejection rates during validation. These metrics help identify processing capacity limits and data quality trends.
-
Peak memory consumption per stage prevents out-of-memory failures. Filtering operations might spike memory usage when loading large intermediate results. Aggregation stages require materializing grouped data. Memory patterns vary dramatically based on dataset characteristics and processing logic.
-
Error paths and panics in Rust code need special attention. Unlike garbage-collected languages, Rust memory safety prevents many runtime failures, but data pipeline errors often relate to schema mismatches, missing files, or invalid data formats. Capturing error context enables rapid debugging.
These metrics differ from typical web application monitoring because data pipelines process discrete batches rather than continuous streams, consume resources in predictable patterns based on dataset size, and fail differently than request-response applications.
Traditional application observability focuses on user-facing performance and system health. Data pipeline observability focuses on data quality, processing efficiency, and resource utilization patterns that affect downstream systems and analysis accuracy.
Adding Observability to Rust ETL Pipeline with OpenTelemetry
Instrumenting Rust applications for data pipeline monitoring requires strategic placement of tracing spans and metrics collection. The observability approach needs to capture processing stage performance, memory usage patterns, and data quality indicators without significantly impacting pipeline throughput.
Here's how to structure observability in your Rust data pipeline using OpenTelemetry and tracing:
-
Spans with contextual attributes provide distributed tracing capability. Each processing stage gets its own span with resource attributes like stage, file, and processing metadata. This enables tracing the whole process across complex pipeline operations.
-
Instrumented functions using the
#[instrument]
attribute automatically capture function entry/exit, execution time, and error conditions. The tracing framework handles span lifecycle management and context propagation. -
Metrics emission follows OpenTelemetry conventions with structured field names like
etl.load.duration
andetl.clean.memory_mb
. These metrics integrate with observability platforms for dashboard creation and alerting.
Memory monitoring through rss_mb()
reads actual RSS memory consumption from /proc/self/status
, providing accurate memory usage data during each processing stage. This approach works reliably in containerized environments and cloud deployments.
⚠️ Platform-Specific Code: This memory monitoring function reads from the /proc
filesystem, which is specific to Linux. For cross-platform compatibility, consider using a crate like sysinfo
that provides unified system information APIs across Windows, macOS, and Linux.
The image below shows the memory usage monitoring for Polars, demonstrating increased memory consumption during processing.



These images show the system monitoring during ETL pipeline execution, where the first image displays baseline performance with low CPU utilization (1-8%) and stable 5.1GB memory usage, the next image shows increased processing activity with CPU8 reaching 32.4% while memory remains stable, and the last one captures peak ETL operations with significantly higher CPU utilization (CPU8 at 85.9%) and elevated memory consumption at 9.9GB, effectively demonstrating how the Rust pipeline scales resource usage during different processing phases and validating the importance of memory monitoring.
By establishing these observability goals upfront, tracking performance, memory usage, and data quality, you create a foundation for reliable monitoring.
Exporting Observability Data to BetterStack via Shuttle
Shuttle provides out-of-the-box OpenTelemetry integration that eliminates traditional observability infrastructure complexity. Instead of configuring collectors, managing exporters, or writing YAML configurations, you get automatic telemetry data export to your chosen observability platform.
To enable this integration, include Shuttle's setup-otel-exporter feature in your runtime configuration:
Then configure observability in your application code:
Shuttle's approach eliminates the infrastructure complexity typically associated with observability. No Docker containers running collectors, no YAML configuration files for exporters, no manual OTLP endpoint management. The platform handles OpenTelemetry protocol export, authentication, and routing automatically.
When you enable BetterStack integration in your Shuttle project settings, telemetry data flows directly from your Rust application to BetterStack's ingestion endpoints. The integration includes automatic retry logic, batching for performance, and error handling that would normally require manual configuration.
This approach works because Shuttle's runtime includes a tracer provider that automatically exports spans and metrics when observability integrations are enabled. Your application code focuses on emitting telemetry data using standard OpenTelemetry patterns, while the platform handles infrastructure concerns.


The images above demonstrate the BetterStack observability platform receiving telemetry data from the Rust ETL pipeline, where the first screen shows basic log streams with application startup events including tracing subscriber initialization and service startup messages, while the second reveals detailed OpenTelemetry span data displaying trace IDs, span IDs, and stage timing information, showcasing how the integration captures both high-level application logs and granular distributed tracing data for comprehensive pipeline monitoring.
Deploying the Observable Pipeline with Shuttle
Shuttle deployment transforms your observable Rust application into a production service with a single command. The platform's infrastructure-from-code approach means your deployment configuration lives in your application rather than external configuration files. The deployment process is broken down into steps:
Step 1: Set up your application for Shuttle deployment:
Step 2: Deploy your observable pipeline:
Step 3: Testing endpoints confirms telemetry data flows correctly:
Shuttle automatically streams metrics to BetterStack once you configure the integration in your project settings. The platform handles authentication, retry logic, and batching that would normally require manual OTLP configuration.







The above images demonstrate the complete development-to-production workflow for the observable Rust ETL pipeline. The initial screens show local CLI execution with structured JSON logging, revealing detailed performance metrics including 108-second total runtime and stage-by-stage timing breakdown, while subsequent displays present comprehensive telemetry output with dataset information, processing statistics of 12.7M records processed at 4.5M records/second, and deployment configuration details. The workflow continues with the deployed Shuttle application's API endpoints for health checks and benchmarking, followed by captures of the Shuttle deployment process and dashboard showing the running application with telemetry integration and log streaming capabilities, further showing how the same codebase provides both detailed local benchmarking and production-ready observability through feature flags and deployment automation.
Building Dashboards for ETL Monitoring in BetterStack
When running ETL pipelines, it's not enough to just know they work; you need visibility into how they perform over time. BetterStack dashboards provide that visibility at a glance by transforming your telemetry data into actionable monitoring dashboards. The platform automatically recognises the metric naming patterns from your Rust application and enables sophisticated visualization and alerting capabilities.
Here are some common ETL-specific metrics and their naming conventions you should follow:
- etl.load.duration: measures the time spent loading CSV
- etl.clean.duration: tracks the performance of data cleaning stage
- etl.aggregate.duration: captures how long aggregation operation takes
- etl.rows.processed: shows record throughput and overall volume
- etl.memory.peak: reports peak memory consumption per stage
You can configure dashboard for data pipeline monitoring like:
Memory Usage Over Time Dashboard:
Stage-by-Stage Duration Analysis:
Processing Throughput Tracking:
These visualizations provide insights into processing performance trends, memory consumption patterns during different pipeline stages, and throughput characteristics that help with capacity planning and performance optimization.


These images show Betterstack monitoring dashboards tracking ETL pipeline performance, where the Telemetry interface displays Clean Time and Aggregate Time charts with regular periodic spikes indicating scheduled batch operations, while a separate CPU usage chart reveals sustained high utilization around 1,200-1,500T during data processing, providing essential visibility into pipeline performance and resource monitoring.
With BetterStack's alerting capabilities, you can enable proactive monitoring by setting alerts for memory usage exceeding thresholds, processing duration degradation, or throughput drops below expected levels with integration into incident management systems for automated escalation.
Local Development and Testing Setup
So, how can you test and benchmark this pipeline on your own local machine with a real dataset? Running the full ETL process within our deployed web service isn't practical; web endpoints need fast responses, not long-running data processing jobs. Instead, we'll use Rust's feature flags to create a separate CLI for local, in-depth testing. This approach lets you experiment with different datasets, measure actual performance characteristics, and validate your observability setup before deploying to production.
Feature flags enable different build targets for local development versus production deployment. This approach allows for comprehensive local testing with real datasets while maintaining production builds as lightweight and cost-effective as possible.
Configure feature-based compilation in Cargo.toml:
Local development implementation with full ETL processing:
CLI workflow for benchmarking locally with comprehensive logs and metrics:
Dataset loading via CLI enables testing with different data characteristics. The CLI version processes actual CSV files and provides accurate performance measurements, memory usage patterns, and processing throughput data that inform production capacity planning.
Local testing reveals performance characteristics that aren't visible in production monitoring. Memory allocation patterns during aggregation stages, processing bottlenecks with different dataset sizes, and error handling behaviour with malformed data files.
Wrapping Up: Fast Pipelines with Precise Monitoring
Rust with Polars delivers consistent performance that traditional Python workflows often lack. While Python pipelines struggle with unpredictable memory spikes, Rust provides predictable resource usage that scales linearly with dataset size. Moreover, compile-time error checking prevents the runtime failures that typically corrupt downstream analysis in dynamically-typed environments.
Building on this foundation, OpenTelemetry adds precise monitoring with minimal performance overhead. This enables comprehensive dashboards and proactive alerts that surface issues before they impact data quality. Meanwhile, Shuttle eliminates the deployment complexity that often delays pipeline rollouts, providing zero-config OTLP export, automatic retry handling, and managed authentication out of the box.
The business impact is substantial: teams ship data products 3x faster without infrastructure bottlenecks, process larger datasets with predictable costs, and maintain higher system reliability that directly improves user experience. Together, these tools create production-ready workflows that deliver fast processing through Rust and Polars, comprehensive visibility via OpenTelemetry, and frictionless deployment through Shuttle. This proactive monitoring approach prevents costly memory failures, enables accurate capacity planning, and catches performance bottlenecks before they affect end users.
Ready to build observable data pipelines? Try the complete example on Shuttle with one-click deployment: Deploy Observable Pipeline
Troubleshooting Telemetry Issues in Rust Pipelines - FAQ
Q: My metrics aren't showing up in BetterStack. What's wrong?
A: First, double-check your integration settings in the Shuttle console and verify your source token hasn't expired. Second, confirm your metric names in the code (e.g., etl.load.duration
) exactly match your dashboard queries—BetterStack requires precise field name matching. Finally, redeploy your application after making integration changes, as the OTLP exporter configuration updates during deployment. Test the flow by running curl https://your-app.shuttle.app/benchmark
and checking if logs appear in BetterStack's log stream before expecting dashboard data.
Q: Why are my spans missing or not connecting in distributed traces?
A: Check that your OpenTelemetry dependencies use compatible versions; mismatches between tracing-opentelemetry
and opentelemetry
crates can break span context propagation. Ensure your instrumented functions include appropriate resource attributes like stage
or file
, as missing context reduces trace value. Also, verify your tracer provider configuration includes your service name and resource attributes needed for filtering and grouping in trace analysis.
Q: My pipeline works in production but fails during local testing. What should I check?
A: Start by validating CSV file locations and permissions—your application needs read access to data files and write access to results directories. Check that your dataset format is compatible with Polars CSV parsing, as schema mismatches or encoding issues can cause silent failures. Monitor memory consumption during testing since large datasets might exceed available memory during aggregation stages. Finally, debug your environment variable configuration, ensuring DATA_PATH
points to accessible file locations.
Q: How can I get better error information when my pipeline fails?
A: Implement structured error logging that includes file paths, record counts, and processing stage information in error messages. This context enables rapid issue resolution by showing exactly where and why failures occur, rather than generic error messages that require extensive debugging to understand.