gcp-rust-tools 0.2.2

Comprehensive Google Cloud Platform tools for Rust: Observability (Logs, Metrics, Traces) and PubSub
Documentation
# GCP Rust Tools

[![Crates.io](https://img.shields.io/crates/v/gcp-rust-tools.svg)](https://crates.io/crates/gcp-rust-tools)
[![Documentation](https://docs.rs/gcp-rust-tools/badge.svg)](https://docs.rs/gcp-rust-tools)

A comprehensive Rust toolset for Google Cloud Platform, combining simplified Observability (Logs, Metrics, Traces) with robust Pub/Sub wrappers.

## Builder notes

This crate was developed by [Santiago Amoretti](https://github.com/santiamoretti) in the context of the development of Genevabm(https://genevabm.com).

## Features

- **πŸš€ Pub/Sub Integration**: Easy wrapper around official Google Cloud Pub/Sub crates.
- **πŸ“ Cloud Logging**: Send structured logs to Google Cloud Logging.
- **πŸ“Š Cloud Monitoring**: Create custom metrics in Google Cloud Monitoring.
- **πŸ” Cloud Trace**: Create distributed traces in Google Cloud Trace.
- **⚑ High Performance**: Designed for efficiency.
- **πŸ›‘οΈ Error Resilient**: Automatic retry logic.

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
gcp-rust-tools = "0.2.0"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```

## Prerequisites

1. **Google Cloud Project** with APIs enabled:
   - Cloud Logging API
   - Cloud Monitoring API
   - Cloud Trace API

2. **Service Account JSON** with roles:
   - `roles/logging.logWriter`
   - `roles/monitoring.metricWriter`
   - `roles/cloudtrace.agent`

3. **gcloud CLI** (automatically installed if missing)

## Architecture

The library uses a channel-based architecture for optimal performance:

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Your App    │────────▢│ Channel │────────▢│ Worker Threadβ”‚
β”‚ (main)      β”‚ queue   β”‚ (1027)  β”‚ process β”‚ (async ops)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                    β”‚
                                                    β–Ό
                                            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                            β”‚  GCP APIs    β”‚
                                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

## Quick Start

### Fire-and-Forget (Recommended)

```rust
use gcp_rust_tools::{ObservabilityClient, LogEntry, MetricData, TraceSpan};
use std::collections::HashMap;
use std::time::{SystemTime, Duration};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize client (performs authentication)
    // Credentials are resolved internally from GOOGLE_APPLICATION_CREDENTIALS.
    // Project id can be provided, or inferred via GOOGLE_CLOUD_PROJECT / gcloud.
    let client = ObservabilityClient::new(
        Some("your-project-id".to_string()),
        None,
    ).await?;

    // Fire-and-forget logging (returns immediately)
    client.send_log(LogEntry::new("INFO", "App started"))?;
    
    // With service name
    client.send_log(
        LogEntry::new("ERROR", "DB connection failed")
            .with_service_name("api-server")
    )?;

    // Send metrics with labels
    let mut labels = HashMap::new();
    labels.insert("environment".to_string(), "production".to_string());
    
    client.send_metric(
        MetricData::new(
            "custom.googleapis.com/requests_total",
            42.0,
            "INT64",
            "GAUGE"
        ).with_labels(labels)
    )?;

    // Create distributed traces
    let trace_id = ObservabilityClient::generate_trace_id();
    let span_id = ObservabilityClient::generate_span_id();
    
    client.send_trace(
        TraceSpan::new(
            trace_id,
            span_id,
            "HTTP Request",
            SystemTime::now(),
            Duration::from_millis(150)
        )
    )?;

    Ok(())
}
```

### Async (Wait for Completion)

When you need confirmation that an operation succeeded:

```rust
// Wait for operation to complete
client.send_log_async(LogEntry::new("INFO", "Critical log")).await?;
client.send_metric_async(MetricData::new("metric", 1.0, "INT64", "GAUGE")).await?;
client.send_trace_async(TraceSpan::new(...)).await?;
```

### Using Convenience Macros

```rust
use gcp_rust_tools::{gcp_info, gcp_warn, gcp_error};

gcp_info!(client, "User {} logged in", user_id)?;
gcp_warn!(client, "High memory usage: {}%", usage)?;
gcp_error!(client, "Failed to process: {}", error)?;
```

## API Reference

### ObservabilityClient

#### Initialization
- `new(project_id, service_account_path)` β†’ `Result<Self, ObservabilityError>`
  - Creates and authenticates a new client
  - Starts background worker thread

#### Fire-and-Forget Methods
- `send_log(log_entry: LogEntry)` β†’ `Result<(), ObservabilityError>`
- `send_metric(metric_data: MetricData)` β†’ `Result<(), ObservabilityError>`
- `send_trace(trace_span: TraceSpan)` β†’ `Result<(), ObservabilityError>`

#### Async Methods (Wait for Completion)
- `send_log_async(log_entry: LogEntry)` β†’ `Future<Result<(), ObservabilityError>>`
- `send_metric_async(metric_data: MetricData)` β†’ `Future<Result<(), ObservabilityError>>`
- `send_trace_async(trace_span: TraceSpan)` β†’ `Future<Result<(), ObservabilityError>>`

#### Utility Methods
- `generate_trace_id()` β†’ `String` - Generate a 32-character hex trace ID
- `generate_span_id()` β†’ `String` - Generate a 16-character hex span ID

### Data Structures

#### LogEntry
```rust
LogEntry::new(severity: impl Into<String>, message: impl Into<String>)
    .with_service_name(name: impl Into<String>)
```

#### MetricData
```rust
MetricData::new(
    metric_type: impl Into<String>,
    value: f64,
    value_type: impl Into<String>,  // "INT64" | "DOUBLE"
    metric_kind: impl Into<String>  // "GAUGE" | "CUMULATIVE"
)
    .with_labels(labels: HashMap<String, String>)
```

#### TraceSpan
```rust
TraceSpan::new(
    trace_id: impl Into<String>,
    span_id: impl Into<String>,
    display_name: impl Into<String>,
    start_time: SystemTime,
    duration: Duration
)
    .with_parent_span_id(parent_span_id: impl Into<String>)
```

### Convenience Macros

- `gcp_info!(client, "message")` - Send an INFO log (fire-and-forget)
- `gcp_warn!(client, "message")` - Send a WARNING log (fire-and-forget)
- `gcp_error!(client, "message")` - Send an ERROR log (fire-and-forget)
- `gcp_log!(client, "LEVEL", "message")` - Send a log with custom severity

## Error Handling

The library provides comprehensive error handling:

### Error Types

- `AuthenticationError` - Failed to authenticate with gcloud
- `ApiError` - Google Cloud API request failed
- `SetupError` - Failed to setup/install gcloud CLI
- `Shutdown` - Special internal error for worker shutdown

### Token Expiration

The library automatically handles token expiration:

1. Detects expired tokens (401/403 HTTP responses)
2. Re-authenticates using your service account
3. Retries the failed operation with a fresh token
4. All happens transparently in the background

### Silent Failures

Background operations fail silently to avoid disrupting your application. If you need error feedback, use the async methods:

```rust
match client.send_log_async(LogEntry::new("INFO", "Important")).await {
    Ok(()) => println!("Log sent successfully"),
    Err(e) => eprintln!("Failed to send log: {}", e),
}
```

## Performance

### Characteristics

- **Non-blocking**: Fire-and-forget operations return immediately
- **Bounded Channel**: 1027-item buffer prevents memory overflow  
- **Single Worker**: One background thread prevents API rate limiting
- **No Synchronization Overhead**: Minimal locking and contention
- **Fast Compilation**: No heavy protobuf or gRPC dependencies

### Benchmarks

On a typical development machine:
- Fire-and-forget operation: < 1Β΅s
- Background processing: ~50-200ms per operation (network dependent)
- Channel capacity: 1027 items before blocking

## Features

```toml
[dependencies]
gcp-observability-rs = { version = "0.1.0", features = ["logging", "monitoring"] }
```

Available features:
- `logging` - Cloud Logging functionality
- `monitoring` - Cloud Monitoring functionality  
- `tracing` - Cloud Trace functionality
- `default` - Includes all features

## Examples

Run the example:

```bash
# Set your project ID and service account path
export GCP_PROJECT_ID="your-project-id"
export GCP_SERVICE_ACCOUNT="/path/to/service-account.json"

cargo run --example basic_usage
```

## Architecture

This library uses a unique approach that balances simplicity with performance:

- **Lightweight**: No heavy protobuf or gRPC dependencies
- **Simple**: Uses standard HTTP/REST APIs via curl
- **Reliable**: Leverages battle-tested gcloud CLI for authentication
- **Fast**: Minimal overhead and fast compilation times
- **Resilient**: Automatic token refresh and retry logic

### Background Worker

The single-threaded worker model provides natural rate limiting:
- One thread processes all operations sequentially
- Prevents overwhelming GCP APIs
- No complex rate limiting logic needed
- Predictable memory usage

## Troubleshooting

### Authentication Issues

```bash
# Verify gcloud is installed
gcloud version

# Verify service account works
gcloud auth activate-service-account --key-file=/path/to/key.json
gcloud auth list
```

### API Not Enabled

Enable required APIs in your GCP project:
```bash
gcloud services enable logging.googleapis.com
gcloud services enable monitoring.googleapis.com
gcloud services enable cloudtrace.googleapis.com
```

### Permission Issues

Ensure your service account has the required roles:
- `roles/logging.logWriter`
- `roles/monitoring.metricWriter`
- `roles/cloudtrace.agent`

## License

Licensed under the Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0).

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.