mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-08 06:13:14 +00:00
c5df1f92c2
* improve code for notify * improve code for logger and fix typo (#272) * Add GNU to build.yml (#275) * fix unzip error * fix url change error fix url change error * Simplify user experience and integrate console and endpoint Simplify user experience and integrate console and endpoint * Add gnu to build.yml * upgrade version * feat: add `cargo clippy --fix --allow-dirty` to pre-commit command (#282) Resolves #277 - Add --fix flag to automatically fix clippy warnings - Add --allow-dirty flag to run on dirty Git trees - Improves code quality in pre-commit workflow * fix: the issue where preview fails when the path length exceeds 255 characters (#280) * fix * fix: improve Windows build support and CI/CD workflow (#283) - Fix Windows zip command issue by using PowerShell Compress-Archive - Add Windows support for OSS upload with ossutil - Replace Chinese comments with English in build.yml - Fix bash syntax error in package_zip function - Improve code formatting and consistency - Update various configuration files for better cross-platform support Resolves Windows build failures in GitHub Actions. * fix: update link in README.md leading to a 404 error (#285) * add rustfs.spec for rustfs (#103) add support on loongarch64 * improve cargo.lock * build(deps): bump the dependencies group with 5 updates (#289) Bumps the dependencies group with 5 updates: | Package | From | To | | --- | --- | --- | | [hyper-util](https://github.com/hyperium/hyper-util) | `0.1.15` | `0.1.16` | | [rand](https://github.com/rust-random/rand) | `0.9.1` | `0.9.2` | | [serde_json](https://github.com/serde-rs/json) | `1.0.140` | `1.0.141` | | [strum](https://github.com/Peternator7/strum) | `0.27.1` | `0.27.2` | | [sysinfo](https://github.com/GuillaumeGomez/sysinfo) | `0.36.0` | `0.36.1` | Updates `hyper-util` from 0.1.15 to 0.1.16 - [Release notes](https://github.com/hyperium/hyper-util/releases) - [Changelog](https://github.com/hyperium/hyper-util/blob/master/CHANGELOG.md) - [Commits](https://github.com/hyperium/hyper-util/compare/v0.1.15...v0.1.16) Updates `rand` from 0.9.1 to 0.9.2 - [Release notes](https://github.com/rust-random/rand/releases) - [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-random/rand/compare/rand_core-0.9.1...rand_core-0.9.2) Updates `serde_json` from 1.0.140 to 1.0.141 - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.140...v1.0.141) Updates `strum` from 0.27.1 to 0.27.2 - [Release notes](https://github.com/Peternator7/strum/releases) - [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md) - [Commits](https://github.com/Peternator7/strum/compare/v0.27.1...v0.27.2) Updates `sysinfo` from 0.36.0 to 0.36.1 - [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md) - [Commits](https://github.com/GuillaumeGomez/sysinfo/compare/v0.36.0...v0.36.1) --- updated-dependencies: - dependency-name: hyper-util dependency-version: 0.1.16 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: rand dependency-version: 0.9.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: serde_json dependency-version: 1.0.141 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: strum dependency-version: 0.27.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: sysinfo dependency-version: 0.36.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * improve code for logger * improve * upgrade * refactor: 优化构建工作流,统一 latest 文件处理和简化制品上传 (#293) * Refactor: DatabaseManagerSystem as global Signed-off-by: junxiang Mu <1948535941@qq.com> * fix: fmt Signed-off-by: junxiang Mu <1948535941@qq.com> * Test: add e2e_test for s3select Signed-off-by: junxiang Mu <1948535941@qq.com> * Test: add test script for e2e Signed-off-by: junxiang Mu <1948535941@qq.com> * improve code for registry and intergation * improve code for registry `create_targets_from_config` * fix * Feature up/ilm (#305) * fix * fix * fix * fix delete-marker expiration. add api_restore. * fix * time retry object upload * lock file * make fmt * fix * restore object * fix * fix * serde-rs-xml -> quick-xml * fix * checksum * fix * fix * fix * fix * fix * fix * fix * transfer lang to english * upgrade clap version from 4.5.41 to 4.5.42 * refactor: replace `lazy_static` with `LazyLock` * add router * fix: modify comment * improve code * fix typos * fix * fix: modify name and fmt * improve code for registry * fix test --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: junxiang Mu <1948535941@qq.com> Co-authored-by: loverustfs <155562731+loverustfs@users.noreply.github.com> Co-authored-by: 安正超 <anzhengchao@gmail.com> Co-authored-by: shiro.lee <69624924+shiroleeee@users.noreply.github.com> Co-authored-by: Marco Orlandin <mipnamic@mipnamic.net> Co-authored-by: zhangwenlong <zhangwenlong@loongson.cn> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: junxiang Mu <1948535941@qq.com> Co-authored-by: likewu <likewu@126.com>
476 lines
19 KiB
Rust
476 lines
19 KiB
Rust
// Copyright 2024 RustFS Team
|
|
//
|
|
// 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.
|
|
|
|
use crate::OtelConfig;
|
|
use flexi_logger::{Age, Cleanup, Criterion, DeferredNow, FileSpec, LogSpecification, Naming, Record, WriteMode, style};
|
|
use nu_ansi_term::Color;
|
|
use opentelemetry::trace::TracerProvider;
|
|
use opentelemetry::{KeyValue, global};
|
|
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
|
|
use opentelemetry_otlp::WithExportConfig;
|
|
use opentelemetry_sdk::logs::SdkLoggerProvider;
|
|
use opentelemetry_sdk::{
|
|
Resource,
|
|
metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider},
|
|
trace::{RandomIdGenerator, Sampler, SdkTracerProvider},
|
|
};
|
|
use opentelemetry_semantic_conventions::{
|
|
SCHEMA_URL,
|
|
attribute::{DEPLOYMENT_ENVIRONMENT_NAME, NETWORK_LOCAL_ADDRESS, SERVICE_VERSION as OTEL_SERVICE_VERSION},
|
|
};
|
|
use rustfs_config::observability::ENV_OBS_LOG_DIRECTORY;
|
|
use rustfs_config::{
|
|
APP_NAME, DEFAULT_LOG_KEEP_FILES, DEFAULT_LOG_LEVEL, ENVIRONMENT, METER_INTERVAL, SAMPLE_RATIO, SERVICE_VERSION, USE_STDOUT,
|
|
};
|
|
use rustfs_utils::get_local_ip_with_default;
|
|
use smallvec::SmallVec;
|
|
use std::borrow::Cow;
|
|
use std::fs;
|
|
use std::io::IsTerminal;
|
|
use tracing::info;
|
|
use tracing_error::ErrorLayer;
|
|
use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer};
|
|
use tracing_subscriber::fmt::format::FmtSpan;
|
|
use tracing_subscriber::fmt::time::LocalTime;
|
|
use tracing_subscriber::{EnvFilter, Layer, layer::SubscriberExt, util::SubscriberInitExt};
|
|
|
|
/// A guard object that manages the lifecycle of OpenTelemetry components.
|
|
///
|
|
/// This struct holds references to the created OpenTelemetry providers and ensures
|
|
/// they are properly shut down when the guard is dropped. It implements the RAII
|
|
/// (Resource Acquisition Is Initialization) pattern for managing telemetry resources.
|
|
///
|
|
/// When this guard goes out of scope, it will automatically shut down:
|
|
/// - The tracer provider (for distributed tracing)
|
|
/// - The meter provider (for metrics collection)
|
|
/// - The logger provider (for structured logging)
|
|
// Implement Debug trait correctly, rather than using derive, as some fields may not have implemented Debug
|
|
pub struct OtelGuard {
|
|
tracer_provider: Option<SdkTracerProvider>,
|
|
meter_provider: Option<SdkMeterProvider>,
|
|
logger_provider: Option<SdkLoggerProvider>,
|
|
// Add a flexi_logger handle to keep the logging alive
|
|
_flexi_logger_handles: Option<flexi_logger::LoggerHandle>,
|
|
}
|
|
|
|
// Implement debug manually and avoid relying on all fields to implement debug
|
|
impl std::fmt::Debug for OtelGuard {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("OtelGuard")
|
|
.field("tracer_provider", &self.tracer_provider.is_some())
|
|
.field("meter_provider", &self.meter_provider.is_some())
|
|
.field("logger_provider", &self.logger_provider.is_some())
|
|
.field("_flexi_logger_handles", &self._flexi_logger_handles.is_some())
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl Drop for OtelGuard {
|
|
fn drop(&mut self) {
|
|
if let Some(provider) = self.tracer_provider.take() {
|
|
if let Err(err) = provider.shutdown() {
|
|
eprintln!("Tracer shutdown error: {err:?}");
|
|
}
|
|
}
|
|
|
|
if let Some(provider) = self.meter_provider.take() {
|
|
if let Err(err) = provider.shutdown() {
|
|
eprintln!("Meter shutdown error: {err:?}");
|
|
}
|
|
}
|
|
if let Some(provider) = self.logger_provider.take() {
|
|
if let Err(err) = provider.shutdown() {
|
|
eprintln!("Logger shutdown error: {err:?}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// create OpenTelemetry Resource
|
|
fn resource(config: &OtelConfig) -> Resource {
|
|
Resource::builder()
|
|
.with_service_name(Cow::Borrowed(config.service_name.as_deref().unwrap_or(APP_NAME)).to_string())
|
|
.with_schema_url(
|
|
[
|
|
KeyValue::new(
|
|
OTEL_SERVICE_VERSION,
|
|
Cow::Borrowed(config.service_version.as_deref().unwrap_or(SERVICE_VERSION)).to_string(),
|
|
),
|
|
KeyValue::new(
|
|
DEPLOYMENT_ENVIRONMENT_NAME,
|
|
Cow::Borrowed(config.environment.as_deref().unwrap_or(ENVIRONMENT)).to_string(),
|
|
),
|
|
KeyValue::new(NETWORK_LOCAL_ADDRESS, get_local_ip_with_default()),
|
|
],
|
|
SCHEMA_URL,
|
|
)
|
|
.build()
|
|
}
|
|
|
|
/// Creates a periodic reader for stdout metrics
|
|
fn create_periodic_reader(interval: u64) -> PeriodicReader<opentelemetry_stdout::MetricExporter> {
|
|
PeriodicReader::builder(opentelemetry_stdout::MetricExporter::default())
|
|
.with_interval(std::time::Duration::from_secs(interval))
|
|
.build()
|
|
}
|
|
|
|
/// Initialize Telemetry
|
|
pub(crate) fn init_telemetry(config: &OtelConfig) -> OtelGuard {
|
|
// avoid repeated access to configuration fields
|
|
let endpoint = &config.endpoint;
|
|
let use_stdout = config.use_stdout.unwrap_or(USE_STDOUT);
|
|
let meter_interval = config.meter_interval.unwrap_or(METER_INTERVAL);
|
|
let logger_level = config.logger_level.as_deref().unwrap_or(DEFAULT_LOG_LEVEL);
|
|
let service_name = config.service_name.as_deref().unwrap_or(APP_NAME);
|
|
let environment = config.environment.as_deref().unwrap_or(ENVIRONMENT);
|
|
|
|
// Configure flexi_logger to cut by time and size
|
|
let mut flexi_logger_handle = None;
|
|
if !endpoint.is_empty() {
|
|
// Pre-create resource objects to avoid repeated construction
|
|
let res = resource(config);
|
|
|
|
// initialize tracer provider
|
|
let tracer_provider = {
|
|
let sample_ratio = config.sample_ratio.unwrap_or(SAMPLE_RATIO);
|
|
let sampler = if sample_ratio > 0.0 && sample_ratio < 1.0 {
|
|
Sampler::TraceIdRatioBased(sample_ratio)
|
|
} else {
|
|
Sampler::AlwaysOn
|
|
};
|
|
|
|
let builder = SdkTracerProvider::builder()
|
|
.with_sampler(sampler)
|
|
.with_id_generator(RandomIdGenerator::default())
|
|
.with_resource(res.clone());
|
|
|
|
let tracer_provider = if endpoint.is_empty() {
|
|
builder
|
|
.with_batch_exporter(opentelemetry_stdout::SpanExporter::default())
|
|
.build()
|
|
} else {
|
|
let exporter = opentelemetry_otlp::SpanExporter::builder()
|
|
.with_tonic()
|
|
.with_endpoint(endpoint)
|
|
.build()
|
|
.unwrap();
|
|
|
|
let builder = if use_stdout {
|
|
builder
|
|
.with_batch_exporter(exporter)
|
|
.with_batch_exporter(opentelemetry_stdout::SpanExporter::default())
|
|
} else {
|
|
builder.with_batch_exporter(exporter)
|
|
};
|
|
|
|
builder.build()
|
|
};
|
|
|
|
global::set_tracer_provider(tracer_provider.clone());
|
|
tracer_provider
|
|
};
|
|
|
|
// initialize meter provider
|
|
let meter_provider = {
|
|
let mut builder = MeterProviderBuilder::default().with_resource(res.clone());
|
|
|
|
if endpoint.is_empty() {
|
|
builder = builder.with_reader(create_periodic_reader(meter_interval));
|
|
} else {
|
|
let exporter = opentelemetry_otlp::MetricExporter::builder()
|
|
.with_tonic()
|
|
.with_endpoint(endpoint)
|
|
.with_temporality(opentelemetry_sdk::metrics::Temporality::default())
|
|
.build()
|
|
.unwrap();
|
|
|
|
builder = builder.with_reader(
|
|
PeriodicReader::builder(exporter)
|
|
.with_interval(std::time::Duration::from_secs(meter_interval))
|
|
.build(),
|
|
);
|
|
|
|
if use_stdout {
|
|
builder = builder.with_reader(create_periodic_reader(meter_interval));
|
|
}
|
|
}
|
|
|
|
let meter_provider = builder.build();
|
|
global::set_meter_provider(meter_provider.clone());
|
|
meter_provider
|
|
};
|
|
|
|
// initialize logger provider
|
|
let logger_provider = {
|
|
let mut builder = SdkLoggerProvider::builder().with_resource(res);
|
|
|
|
if endpoint.is_empty() {
|
|
builder = builder.with_batch_exporter(opentelemetry_stdout::LogExporter::default());
|
|
} else {
|
|
let exporter = opentelemetry_otlp::LogExporter::builder()
|
|
.with_tonic()
|
|
.with_endpoint(endpoint)
|
|
.build()
|
|
.unwrap();
|
|
|
|
builder = builder.with_batch_exporter(exporter);
|
|
|
|
if use_stdout {
|
|
builder = builder.with_batch_exporter(opentelemetry_stdout::LogExporter::default());
|
|
}
|
|
}
|
|
|
|
builder.build()
|
|
};
|
|
|
|
// configuring tracing
|
|
{
|
|
// configure the formatting layer
|
|
let fmt_layer = {
|
|
let enable_color = std::io::stdout().is_terminal();
|
|
let mut layer = tracing_subscriber::fmt::layer()
|
|
.with_timer(LocalTime::rfc_3339())
|
|
.with_target(true)
|
|
.with_ansi(enable_color)
|
|
.with_thread_names(true)
|
|
.with_thread_ids(true)
|
|
.with_file(true)
|
|
.with_line_number(true);
|
|
|
|
// Only add full span events tracking in the development environment
|
|
if environment != ENVIRONMENT {
|
|
layer = layer.with_span_events(FmtSpan::FULL);
|
|
}
|
|
|
|
layer.with_filter(build_env_filter(logger_level, None))
|
|
};
|
|
|
|
let filter = build_env_filter(logger_level, None);
|
|
let otel_filter = build_env_filter(logger_level, None);
|
|
let otel_layer = OpenTelemetryTracingBridge::new(&logger_provider).with_filter(otel_filter);
|
|
let tracer = tracer_provider.tracer(Cow::Borrowed(service_name).to_string());
|
|
|
|
// Configure registry to avoid repeated calls to filter methods
|
|
tracing_subscriber::registry()
|
|
.with(filter)
|
|
.with(ErrorLayer::default())
|
|
.with(if config.local_logging_enabled.unwrap_or(false) {
|
|
Some(fmt_layer)
|
|
} else {
|
|
None
|
|
})
|
|
.with(OpenTelemetryLayer::new(tracer))
|
|
.with(otel_layer)
|
|
.with(MetricsLayer::new(meter_provider.clone()))
|
|
.init();
|
|
|
|
if !endpoint.is_empty() {
|
|
info!(
|
|
"OpenTelemetry telemetry initialized with OTLP endpoint: {}, logger_level: {},RUST_LOG env: {}",
|
|
endpoint,
|
|
logger_level,
|
|
std::env::var("RUST_LOG").unwrap_or_else(|_| "Not set".to_string())
|
|
);
|
|
}
|
|
}
|
|
|
|
OtelGuard {
|
|
tracer_provider: Some(tracer_provider),
|
|
meter_provider: Some(meter_provider),
|
|
logger_provider: Some(logger_provider),
|
|
_flexi_logger_handles: flexi_logger_handle,
|
|
}
|
|
} else {
|
|
// Obtain the log directory and file name configuration
|
|
let default_log_directory = rustfs_utils::dirs::get_log_directory_to_string(ENV_OBS_LOG_DIRECTORY);
|
|
let log_directory = config.log_directory.as_deref().unwrap_or(default_log_directory.as_str());
|
|
let log_filename = config.log_filename.as_deref().unwrap_or(service_name);
|
|
|
|
if let Err(e) = fs::create_dir_all(log_directory) {
|
|
eprintln!("Failed to create log directory {log_directory}: {e}");
|
|
}
|
|
#[cfg(unix)]
|
|
{
|
|
// Linux/macOS Setting Permissions
|
|
// Set the log directory permissions to 755 (rwxr-xr-x)
|
|
use std::fs::Permissions;
|
|
use std::os::unix::fs::PermissionsExt;
|
|
match fs::set_permissions(log_directory, Permissions::from_mode(0o755)) {
|
|
Ok(_) => eprintln!("Log directory permissions set to 755: {log_directory}"),
|
|
Err(e) => eprintln!("Failed to set log directory permissions {log_directory}: {e}"),
|
|
}
|
|
}
|
|
|
|
// Build log cutting conditions
|
|
let rotation_criterion = match (config.log_rotation_time.as_deref(), config.log_rotation_size_mb) {
|
|
// Cut by time and size at the same time
|
|
(Some(time), Some(size)) => {
|
|
let age = match time.to_lowercase().as_str() {
|
|
"hour" => Age::Hour,
|
|
"day" => Age::Day,
|
|
"minute" => Age::Minute,
|
|
"second" => Age::Second,
|
|
_ => Age::Day, // The default is by day
|
|
};
|
|
Criterion::AgeOrSize(age, size * 1024 * 1024) // Convert to bytes
|
|
}
|
|
// Cut by time only
|
|
(Some(time), None) => {
|
|
let age = match time.to_lowercase().as_str() {
|
|
"hour" => Age::Hour,
|
|
"day" => Age::Day,
|
|
"minute" => Age::Minute,
|
|
"second" => Age::Second,
|
|
_ => Age::Day, // The default is by day
|
|
};
|
|
Criterion::Age(age)
|
|
}
|
|
// Cut by size only
|
|
(None, Some(size)) => {
|
|
Criterion::Size(size * 1024 * 1024) // Convert to bytes
|
|
}
|
|
// By default, it is cut by the day
|
|
_ => Criterion::Age(Age::Day),
|
|
};
|
|
|
|
// The number of log files retained
|
|
let keep_files = config.log_keep_files.unwrap_or(DEFAULT_LOG_KEEP_FILES);
|
|
|
|
// Parsing the log level
|
|
let log_spec = LogSpecification::parse(logger_level).unwrap_or(LogSpecification::info());
|
|
|
|
// Convert the logger_level string to the corresponding LevelFilter
|
|
let level_filter = match logger_level.to_lowercase().as_str() {
|
|
"trace" => flexi_logger::Duplicate::Trace,
|
|
"debug" => flexi_logger::Duplicate::Debug,
|
|
"info" => flexi_logger::Duplicate::Info,
|
|
"warn" | "warning" => flexi_logger::Duplicate::Warn,
|
|
"error" => flexi_logger::Duplicate::Error,
|
|
"off" => flexi_logger::Duplicate::None,
|
|
_ => flexi_logger::Duplicate::Info, // the default is info
|
|
};
|
|
|
|
// Configure the flexi_logger
|
|
let flexi_logger_result = flexi_logger::Logger::try_with_env_or_str(logger_level)
|
|
.unwrap_or_else(|e| {
|
|
eprintln!("Invalid logger level: {logger_level}, using default: {DEFAULT_LOG_LEVEL}, failed error: {e:?}");
|
|
flexi_logger::Logger::with(log_spec.clone())
|
|
})
|
|
.log_to_file(
|
|
FileSpec::default()
|
|
.directory(log_directory)
|
|
.basename(log_filename)
|
|
.suppress_timestamp(),
|
|
)
|
|
.rotate(rotation_criterion, Naming::TimestampsDirect, Cleanup::KeepLogFiles(keep_files.into()))
|
|
.format_for_files(format_for_file) // Add a custom formatting function for file output
|
|
.duplicate_to_stdout(level_filter) // Use dynamic levels
|
|
.format_for_stdout(format_with_color) // Add a custom formatting function for terminal output
|
|
.write_mode(WriteMode::BufferAndFlush)
|
|
.append() // Avoid clearing existing logs at startup
|
|
.print_message() // Startup information output to console
|
|
.start();
|
|
|
|
if let Ok(logger) = flexi_logger_result {
|
|
// Save the logger handle to keep the logging
|
|
flexi_logger_handle = Some(logger);
|
|
|
|
eprintln!("Flexi logger initialized with file logging to {log_directory}/{log_filename}.log");
|
|
|
|
// Log logging of log cutting conditions
|
|
match (config.log_rotation_time.as_deref(), config.log_rotation_size_mb) {
|
|
(Some(time), Some(size)) => eprintln!(
|
|
"Log rotation configured for: every {time} or when size exceeds {size}MB, keeping {keep_files} files"
|
|
),
|
|
(Some(time), None) => eprintln!("Log rotation configured for: every {time}, keeping {keep_files} files"),
|
|
(None, Some(size)) => {
|
|
eprintln!("Log rotation configured for: when size exceeds {size}MB, keeping {keep_files} files")
|
|
}
|
|
_ => eprintln!("Log rotation configured for: daily, keeping {keep_files} files"),
|
|
}
|
|
} else {
|
|
eprintln!("Failed to initialize flexi_logger: {:?}", flexi_logger_result.err());
|
|
}
|
|
|
|
OtelGuard {
|
|
tracer_provider: None,
|
|
meter_provider: None,
|
|
logger_provider: None,
|
|
_flexi_logger_handles: flexi_logger_handle,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn build_env_filter(logger_level: &str, default_level: Option<&str>) -> EnvFilter {
|
|
let level = default_level.unwrap_or(logger_level);
|
|
let mut filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(level));
|
|
if !matches!(logger_level, "trace" | "debug") {
|
|
let directives: SmallVec<[&str; 5]> = smallvec::smallvec!["hyper", "tonic", "h2", "reqwest", "tower"];
|
|
for directive in directives {
|
|
filter = filter.add_directive(format!("{directive}=off").parse().unwrap());
|
|
}
|
|
}
|
|
|
|
filter
|
|
}
|
|
|
|
/// Custom Log Formatter Function - Terminal Output (with Color)
|
|
#[inline(never)]
|
|
fn format_with_color(w: &mut dyn std::io::Write, now: &mut DeferredNow, record: &Record) -> Result<(), std::io::Error> {
|
|
let level = record.level();
|
|
let level_style = style(level);
|
|
|
|
// Get the current thread information
|
|
let binding = std::thread::current();
|
|
let thread_name = binding.name().unwrap_or("unnamed");
|
|
let thread_id = format!("{:?}", std::thread::current().id());
|
|
|
|
writeln!(
|
|
w,
|
|
"[{}] {} [{}] [{}:{}] [{}:{}] {}",
|
|
now.now().format(flexi_logger::TS_DASHES_BLANK_COLONS_DOT_BLANK),
|
|
level_style.paint(level.to_string()),
|
|
Color::Magenta.paint(record.target()),
|
|
Color::Blue.paint(record.file().unwrap_or("unknown")),
|
|
Color::Blue.paint(record.line().unwrap_or(0).to_string()),
|
|
Color::Green.paint(thread_name),
|
|
Color::Green.paint(thread_id),
|
|
record.args()
|
|
)
|
|
}
|
|
|
|
/// Custom Log Formatter - File Output (No Color)
|
|
#[inline(never)]
|
|
fn format_for_file(w: &mut dyn std::io::Write, now: &mut DeferredNow, record: &Record) -> Result<(), std::io::Error> {
|
|
let level = record.level();
|
|
|
|
// Get the current thread information
|
|
let binding = std::thread::current();
|
|
let thread_name = binding.name().unwrap_or("unnamed");
|
|
let thread_id = format!("{:?}", std::thread::current().id());
|
|
|
|
writeln!(
|
|
w,
|
|
"[{}] {} [{}] [{}:{}] [{}:{}] {}",
|
|
now.now().format(flexi_logger::TS_DASHES_BLANK_COLONS_DOT_BLANK),
|
|
level,
|
|
record.target(),
|
|
record.file().unwrap_or("unknown"),
|
|
record.line().unwrap_or(0),
|
|
thread_name,
|
|
thread_id,
|
|
record.args()
|
|
)
|
|
}
|