| Crates.io | blinq-common |
| lib.rs | blinq-common |
| version | 0.1.0 |
| created_at | 2025-06-11 08:59:29.678194+00 |
| updated_at | 2025-06-11 08:59:29.678194+00 |
| description | Common utilities for Blinq Rust microservices: error handling, logging, and HTTP middleware |
| homepage | https://github.com/blinq-dev/blinq-common |
| repository | https://github.com/blinq-dev/blinq-common |
| max_upload_size | |
| id | 1708338 |
| size | 94,011 |
Common utilities for Blinq Rust microservices: error handling, advanced logging with request ID tracking, and HTTP middleware with extensive customization and override capabilities.
AppError type with HTTP status mappingdefault: Enables logging and error-handlingaxum: Axum web framework integrationlogging: Advanced logging utilities with tracing-subscriber and request ID supporterror-handling: Common error types and handlingmiddleware: HTTP middleware utilitiescors: CORS middleware (requires middleware)tracing: HTTP tracing middleware (requires middleware)Add to your microservice's Cargo.toml:
[dependencies]
blinq-common = { version = "0.1.0", features = ["axum", "middleware", "cors", "tracing"] }
Or for minimal usage:
[dependencies]
blinq-common = { version = "0.1.0", features = ["error-handling"] }
use blinq_common::prelude::*;
#[tokio::main]
async fn main() {
// Initialize logging
blinq_common::logging::init();
// Use common error types
let result: Result<String, AppError> = Err(AppError::NotFound("User not found".into()));
}
use blinq_common::prelude::*;
use blinq_common::config::CommonConfig;
use blinq_common::logging::{self, RequestIdStrategy};
use blinq_common::{middleware, named_request_span};
use axum::{routing::get, Router};
use tracing::Instrument;
#[tokio::main]
async fn main() {
// Create advanced logging configuration with request ID support
let logging_config = LoggingConfig::new()
.with_level("debug")
.with_format(LogFormat::Pretty)
.with_service_name("my-service")
.with_auto_request_id(true)
.with_request_id_strategy(RequestIdStrategy::PrefixedUuid("srv".to_string()))
.show_target(true)
.show_thread_ids(true)
.show_file_line(true);
logging::init_advanced(logging_config);
// Create service configuration
let config = CommonConfig::new("my-service")
.with_log_level("debug")
.with_cors_origins(vec!["https://myapp.com".to_string()])
.with_custom("database_url", "postgres://...");
// Use configured middleware
let app = Router::new()
.route("/", get(handler))
.layer(middleware::trace())
.layer(middleware::cors_with_config(&config));
// ... rest of your app
}
async fn handler() -> Result<String, AppError> {
// Create a request span with automatic request ID
let span = named_request_span!("api_request", RequestIdStrategy::Short);
async {
tracing::info!("Processing request");
tracing::debug!("Validating input");
// Your business logic here
tracing::info!("Request processed successfully");
Ok("Hello, World!".to_string())
}
.instrument(span)
.await
}
Automatic request ID injection for distributed tracing across your microservices:
use blinq_common::logging::RequestIdStrategy;
// UUID-like format: "a1b2c3d4-5678-9abc"
RequestIdStrategy::Uuid
// Short alphanumeric: "a1b2c3d4"
RequestIdStrategy::Short
// Timestamp-based: "req_1640995200000"
RequestIdStrategy::Timestamp
// Custom prefix: "pay_a1b2c3d4-5678-9abc"
RequestIdStrategy::PrefixedUuid("pay".to_string())
use blinq_common::{named_request_span, request_span};
use tracing::Instrument;
// Simple request span
let span = request_span!();
// Named request span with custom strategy
let span = named_request_span!("payment_processing", RequestIdStrategy::PrefixedUuid("pay".to_string()));
// Use the span
async {
tracing::info!("Processing payment");
// Your business logic
}
.instrument(span)
.await;
Choose from multiple logging formats based on your environment:
2025-01-15T10:30:45.123456Z INFO payment_service: Payment processed successfully, payment_id: "pay_abc123", amount: 100
at src/payment.rs:45 on ThreadId(2)
in payment_processing with request_id: "pay_def456"
2025-01-15T10:30:45Z INFO payment_processing: Payment processed successfully payment_id="pay_abc123" amount=100 request_id="pay_def456"
{
"timestamp": "2025-01-15T10:30:45Z",
"level": "INFO",
"fields": {
"message": "Payment processed successfully",
"payment_id": "pay_abc123",
"amount": 100
},
"span": { "request_id": "pay_def456", "name": "payment_processing" },
"threadId": "ThreadId(2)"
}
2025-01-15T10:30:45.123456Z INFO ThreadId(2) payment_processing{request_id="pay_def456"}: payment_service: src/payment.rs:45: Payment processed successfully payment_id="pay_abc123" amount=100
use blinq_common::logging::{LoggingConfig, LogFormat, RequestIdStrategy};
// Development configuration
let dev_config = LoggingConfig::new()
.with_level("debug")
.with_format(LogFormat::Pretty)
.with_auto_request_id(true)
.show_target(true)
.show_thread_ids(true)
.show_file_line(true);
// Production configuration
let prod_config = LoggingConfig::new()
.with_level("info")
.with_format(LogFormat::Json)
.with_auto_request_id(true)
.with_request_id_strategy(RequestIdStrategy::PrefixedUuid("prod".to_string()))
.show_target(false)
.show_thread_ids(true)
.show_file_line(false);
logging::init_advanced(prod_config);
Create your own error types that integrate seamlessly:
use blinq_common::AppError;
#[derive(thiserror::Error, Debug)]
pub enum MyServiceError {
#[error("Custom business logic error: {0}")]
BusinessLogicError(String),
#[error("External API error: {0}")]
ExternalApiError(String),
}
// Convert to common error type
impl From<MyServiceError> for AppError {
fn from(err: MyServiceError) -> Self {
match err {
MyServiceError::BusinessLogicError(msg) => AppError::InvalidInput(msg),
MyServiceError::ExternalApiError(msg) => AppError::ExternalServiceError(msg),
}
}
}
let config = CommonConfig::new("api-gateway")
.with_cors_origins(vec![
"https://app.example.com".to_string(),
"https://admin.example.com".to_string(),
])
.with_cors_headers(vec![
"content-type".to_string(),
"authorization".to_string(),
"x-api-key".to_string(),
]);
let config = CommonConfig::new("data-processor")
.with_log_level("trace")
.with_custom("log_format", "json")
.with_custom("log_output", "file")
.with_custom("log_show_target", "false")
.with_custom("log_show_threads", "true")
.with_custom("log_show_location", "true");
// Convert to LoggingConfig
let logging_config = logging::from_common_config(&config);
logging::init_advanced(logging_config);
let config = CommonConfig::new("payment-service")
.with_custom("stripe_api_key", env::var("STRIPE_API_KEY").unwrap())
.with_custom("webhook_secret", env::var("STRIPE_WEBHOOK_SECRET").unwrap())
.with_custom("max_retry_attempts", "3")
.with_custom("enable_request_ids", "true");
error module)AppError: Common error enum with HTTP status mappingIntoResponse implementationservice_error(), is_client_error(), is_server_error()logging module)init(): Basic logging initializationinit_with_config(): Logging with CommonConfiginit_advanced(): Advanced logging with LoggingConfiginit_json_production(): Production JSON logginginit_development(): Development pretty logginginit_test(): Minimal test loggingLoggingConfig: Advanced configuration builderRequestIdStrategy: Request ID generation strategiesgenerate_request_id(): Manual request ID generationcreate_request_span(): Request span creation utilitiesmiddleware module)cors(): Permissive CORS middlewarecors_with_config(): Configurable CORS middlewaretrace(): HTTP request/response tracingconfig module)CommonConfig: Centralized configuration with builder patternRun the basic example:
cargo run --example basic --features="axum,logging,middleware,cors,tracing"
Run the advanced example with request ID tracking:
cargo run --example advanced --features="axum,logging,middleware,cors,tracing"
Test the request ID functionality:
# In one terminal, start the server
cargo run --example advanced --features="axum,logging,middleware,cors,tracing"
# In another terminal, make requests and see unique request IDs in logs
curl http://127.0.0.1:3000/health
curl http://127.0.0.1:3000/payment/100
curl http://127.0.0.1:3000/user/123
Each curl request will generate a unique request ID that appears in all related log entries, making it easy to trace requests through your system.
Here's how to integrate blinq-common into your referral service:
use blinq_common::prelude::*;
use blinq_common::logging::{LoggingConfig, LogFormat, RequestIdStrategy};
use blinq_common::{middleware, named_request_span};
#[tokio::main]
async fn main() {
// Configure logging with request IDs
let logging_config = LoggingConfig::new()
.with_level("info")
.with_format(LogFormat::Json) // For production log aggregation
.with_service_name("referral-service")
.with_auto_request_id(true)
.with_request_id_strategy(RequestIdStrategy::PrefixedUuid("ref".to_string()));
logging::init_advanced(logging_config);
// Service configuration
let config = CommonConfig::new("referral-service")
.with_log_level("info")
.with_cors_origins(vec!["https://app.blinq.com".to_string()])
.with_custom("database_url", "postgres://postgres:postgres@localhost:5432/referral_db");
let app = routes::router()
.layer(Extension(repo))
.layer(middleware::trace())
.layer(middleware::cors_with_config(&config));
// Now every request will have a unique request ID like "ref_a1b2c3d4-5678-9abc"
// that appears in all log entries for that request
}
When using this crate in your microservice:
CommonConfig# Run all tests
cargo test
# Run tests for specific features
cargo test --features="error-handling,logging"
# Test logging formats
cargo run --example logging --features="logging"
See CONTRIBUTING.md for guidelines.
Licensed under the MIT license. See LICENSE for details.