mirror of
https://github.com/minio/minio-rs.git
synced 2025-12-06 15:26:51 +08:00
Adds RequestHooks trait to enable intercepting and modifying S3 API requests at key points in the request lifecycle. This enables implementing cross-cutting concerns like load balancing, telemetry, and debug logging without modifying the core request handling logic. --------- Co-authored-by: Tobias Pütz <tobias@minio.io>
236 lines
7.7 KiB
Rust
236 lines
7.7 KiB
Rust
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
|
|
// Copyright 2024 MinIO, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Example demonstrating how to use RequestHooks for debug logging.
|
|
//!
|
|
//! This example shows:
|
|
//! - Creating a custom debug logging hook
|
|
//! - Attaching the hook to the MinIO client
|
|
//! - Automatic logging of all S3 API requests with headers and response status
|
|
//! - Using both `before_signing_mut` and `after_execute` hooks
|
|
//!
|
|
//! Run with default values (test-bucket / test-object.txt, verbose mode enabled):
|
|
//! ```
|
|
//! cargo run --example debug_logging_hook
|
|
//! ```
|
|
//!
|
|
//! Run with custom bucket and object:
|
|
//! ```
|
|
//! cargo run --example debug_logging_hook -- mybucket myobject
|
|
//! ```
|
|
//!
|
|
//! Disable verbose output:
|
|
//! ```
|
|
//! cargo run --example debug_logging_hook -- --no-verbose
|
|
//! ```
|
|
|
|
use clap::{ArgAction, Parser};
|
|
use futures_util::StreamExt;
|
|
use minio::s3::builders::ObjectContent;
|
|
use minio::s3::client::hooks::{Extensions, RequestHooks};
|
|
use minio::s3::client::{Method, Response};
|
|
use minio::s3::creds::StaticProvider;
|
|
use minio::s3::error::Error;
|
|
use minio::s3::http::Url;
|
|
use minio::s3::multimap_ext::Multimap;
|
|
use minio::s3::response::BucketExistsResponse;
|
|
use minio::s3::segmented_bytes::SegmentedBytes;
|
|
use minio::s3::types::{S3Api, ToStream};
|
|
use minio::s3::{MinioClient, MinioClientBuilder};
|
|
use std::sync::Arc;
|
|
|
|
/// Debug logging hook that prints detailed information about each S3 request.
|
|
#[derive(Debug)]
|
|
struct DebugLoggingHook {
|
|
/// Enable verbose output including all headers
|
|
verbose: bool,
|
|
}
|
|
|
|
impl DebugLoggingHook {
|
|
fn new(verbose: bool) -> Self {
|
|
Self { verbose }
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl RequestHooks for DebugLoggingHook {
|
|
fn name(&self) -> &'static str {
|
|
"debug-logger"
|
|
}
|
|
|
|
async fn before_signing_mut(
|
|
&self,
|
|
method: &Method,
|
|
url: &mut Url,
|
|
_region: &str,
|
|
_headers: &mut Multimap,
|
|
_query_params: &Multimap,
|
|
bucket_name: Option<&str>,
|
|
object_name: Option<&str>,
|
|
_body: Option<&SegmentedBytes>,
|
|
_extensions: &mut Extensions,
|
|
) -> Result<(), Error> {
|
|
if self.verbose {
|
|
let bucket_obj = match (bucket_name, object_name) {
|
|
(Some(b), Some(o)) => format!("{b}/{o}"),
|
|
(Some(b), None) => b.to_string(),
|
|
_ => url.to_string(),
|
|
};
|
|
println!("→ Preparing {method} request for {bucket_obj}");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
async fn after_execute(
|
|
&self,
|
|
method: &Method,
|
|
url: &Url,
|
|
_region: &str,
|
|
headers: &Multimap,
|
|
_query_params: &Multimap,
|
|
bucket_name: Option<&str>,
|
|
object_name: Option<&str>,
|
|
resp: &Result<Response, reqwest::Error>,
|
|
_extensions: &mut Extensions,
|
|
) {
|
|
// Format the basic request info
|
|
let bucket_obj = match (bucket_name, object_name) {
|
|
(Some(b), Some(o)) => format!("{b}/{o}"),
|
|
(Some(b), None) => b.to_string(),
|
|
_ => url.to_string(),
|
|
};
|
|
|
|
// Format response status
|
|
let status = match resp {
|
|
Ok(response) => format!("✓ {}", response.status()),
|
|
Err(err) => format!("✗ Error: {err}"),
|
|
};
|
|
|
|
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
println!("S3 Request: {method} {bucket_obj}");
|
|
println!("URL: {url}");
|
|
println!("Status: {status}");
|
|
|
|
if self.verbose {
|
|
// Print headers alphabetically
|
|
let mut header_strings: Vec<String> = headers
|
|
.iter_all()
|
|
.map(|(k, v)| format!("{}: {}", k, v.join(",")))
|
|
.collect();
|
|
header_strings.sort();
|
|
|
|
println!("\nRequest Headers:");
|
|
for header in header_strings {
|
|
println!(" {header}");
|
|
}
|
|
}
|
|
|
|
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
|
|
}
|
|
}
|
|
|
|
/// Example demonstrating debug logging with hooks
|
|
#[derive(Parser)]
|
|
struct Cli {
|
|
/// Bucket to use for the example
|
|
#[arg(default_value = "test-bucket")]
|
|
bucket: String,
|
|
/// Object to upload
|
|
#[arg(default_value = "test-object.txt")]
|
|
object: String,
|
|
/// Disable verbose output (verbose is enabled by default, use --no-verbose to disable)
|
|
#[arg(long = "no-verbose", action = ArgAction::SetFalse, default_value_t = true)]
|
|
verbose: bool,
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
env_logger::init();
|
|
let args = Cli::parse();
|
|
|
|
println!("\n🔧 MinIO Debug Logging Hook Example\n");
|
|
println!("This example demonstrates how hooks can be used for debugging S3 requests.");
|
|
println!(
|
|
"We'll perform a few operations on bucket '{}' with debug logging enabled.\n",
|
|
args.bucket
|
|
);
|
|
|
|
// Create the debug logging hook
|
|
let debug_hook = Arc::new(DebugLoggingHook::new(args.verbose));
|
|
|
|
// Create MinIO client with the debug logging hook attached
|
|
let static_provider = StaticProvider::new(
|
|
"Q3AM3UQ867SPQQA43P2F",
|
|
"zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
|
|
None,
|
|
);
|
|
|
|
let client: MinioClient = MinioClientBuilder::new("https://play.min.io".parse()?)
|
|
.provider(Some(static_provider))
|
|
.hook(debug_hook) // Attach the debug logging hook
|
|
.build()?;
|
|
|
|
println!("✓ Created MinIO client with debug logging hook\n");
|
|
|
|
// Operation 1: Check if bucket exists
|
|
println!("📋 Checking if bucket exists...");
|
|
let resp: BucketExistsResponse = client.bucket_exists(&args.bucket).build().send().await?;
|
|
|
|
// Operation 2: Create bucket if it doesn't exist
|
|
if !resp.exists() {
|
|
println!("\n📋 Creating bucket...");
|
|
client.create_bucket(&args.bucket).build().send().await?;
|
|
} else {
|
|
println!("\n✓ Bucket already exists");
|
|
}
|
|
|
|
// Operation 3: Upload a small object
|
|
println!("\n📋 Uploading object...");
|
|
let content = b"Hello from MinIO Rust SDK with debug logging!";
|
|
let object_content: ObjectContent = content.to_vec().into();
|
|
client
|
|
.put_object_content(&args.bucket, &args.object, object_content)
|
|
.build()
|
|
.send()
|
|
.await?;
|
|
|
|
// Operation 4: List objects in the bucket
|
|
println!("\n📋 Listing objects in bucket...");
|
|
let mut list_stream = client
|
|
.list_objects(&args.bucket)
|
|
.recursive(false)
|
|
.build()
|
|
.to_stream()
|
|
.await;
|
|
|
|
let mut total_objects = 0;
|
|
while let Some(result) = list_stream.next().await {
|
|
match result {
|
|
Ok(resp) => {
|
|
total_objects += resp.contents.len();
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Error listing objects: {e}");
|
|
}
|
|
}
|
|
}
|
|
println!("\n✓ Found {total_objects} objects in bucket");
|
|
|
|
println!("\n🎉 All operations completed successfully with debug logging enabled!\n");
|
|
println!("💡 Tip: Run with --no-verbose to disable detailed output\n");
|
|
|
|
Ok(())
|
|
}
|