Skip to content

Commit 673d1a9

Browse files
authored
[ISSUE #8090]♻️Report telemetry shutdown health and dropped log lines (#8091)
1 parent 85640dc commit 673d1a9

9 files changed

Lines changed: 307 additions & 39 deletions

File tree

rocketmq-broker/src/bin/broker_bootstrap_server.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ async fn run(service_context: ServiceContext) -> Result<()> {
111111
if let Err(error) = verify_rocketmq_home() {
112112
telemetry_guard
113113
.shutdown()
114+
.into_result()
114115
.context("failed to shutdown broker telemetry bootstrap after ROCKETMQ_HOME validation failed")?;
115116
return Err(error);
116117
}
@@ -120,6 +121,7 @@ async fn run(service_context: ServiceContext) -> Result<()> {
120121
print_config(&broker_config, &message_store_config, args.print_important_config);
121122
telemetry_guard
122123
.shutdown()
124+
.into_result()
123125
.context("failed to shutdown broker telemetry bootstrap after printing broker configuration")?;
124126
return Ok(());
125127
}

rocketmq-broker/src/broker_runtime.rs

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,17 @@ impl BrokerShutdownComponentReport {
520520
}
521521
}
522522

523+
pub(crate) fn completed_with_detail(name: &'static str, elapsed: Duration, detail: impl Into<String>) -> Self {
524+
Self {
525+
name,
526+
present: true,
527+
healthy: true,
528+
timed_out: false,
529+
elapsed,
530+
detail: Some(detail.into()),
531+
}
532+
}
533+
523534
pub(crate) fn unhealthy(name: &'static str, elapsed: Duration, detail: impl Into<String>) -> Self {
524535
Self {
525536
name,
@@ -555,6 +566,18 @@ impl BrokerShutdownComponentReport {
555566
Self::unhealthy(name, elapsed, report.to_json())
556567
}
557568
}
569+
570+
pub(crate) fn from_telemetry_shutdown_report(
571+
report: &rocketmq_observability::TelemetryShutdownReport,
572+
elapsed: Duration,
573+
) -> Self {
574+
let detail = report.to_json();
575+
if report.is_healthy() {
576+
Self::completed_with_detail("observability", elapsed, detail)
577+
} else {
578+
Self::unhealthy("observability", elapsed, detail)
579+
}
580+
}
558581
}
559582

560583
impl BrokerBasicServiceShutdownReport {
@@ -971,14 +994,15 @@ impl BrokerRuntime {
971994
{
972995
let started = Instant::now();
973996
if let Some(guard) = self.inner.observability_guard.take() {
974-
if let Err(error) = guard.shutdown() {
975-
warn!("Failed to shutdown observability runtime: {error}");
976-
shutdown_report.observability =
977-
BrokerShutdownComponentReport::unhealthy("observability", started.elapsed(), error.to_string());
978-
} else {
979-
shutdown_report.observability =
980-
BrokerShutdownComponentReport::completed("observability", started.elapsed());
997+
let telemetry_report = guard.shutdown();
998+
if !telemetry_report.is_healthy() {
999+
warn!(
1000+
report = %telemetry_report.to_json(),
1001+
"Failed to shutdown observability runtime cleanly"
1002+
);
9811003
}
1004+
shutdown_report.observability =
1005+
BrokerShutdownComponentReport::from_telemetry_shutdown_report(&telemetry_report, started.elapsed());
9821006
} else {
9831007
shutdown_report.observability = BrokerShutdownComponentReport::skipped("observability");
9841008
}
@@ -5148,6 +5172,57 @@ mod tests {
51485172
assert_eq!(report.timed_out_component_names(), vec!["message_store"]);
51495173
}
51505174

5175+
#[test]
5176+
fn broker_shutdown_report_records_healthy_telemetry_detail() {
5177+
let telemetry_report = rocketmq_observability::TelemetryShutdownReport {
5178+
subscriber_installed: true,
5179+
file_log_enabled: true,
5180+
dropped_log_lines: 0,
5181+
logs_shutdown_ok: true,
5182+
traces_shutdown_ok: true,
5183+
metrics_shutdown_ok: true,
5184+
logs_shutdown_error: None,
5185+
traces_shutdown_error: None,
5186+
metrics_shutdown_error: None,
5187+
};
5188+
5189+
let component =
5190+
BrokerShutdownComponentReport::from_telemetry_shutdown_report(&telemetry_report, Duration::from_millis(4));
5191+
5192+
assert!(component.present);
5193+
assert!(component.healthy);
5194+
assert_eq!(component.name, "observability");
5195+
let detail = component.detail.expect("telemetry detail should be recorded");
5196+
assert!(detail.contains("\"subscriber_installed\": true"));
5197+
assert!(detail.contains("\"file_log_enabled\": true"));
5198+
}
5199+
5200+
#[test]
5201+
fn broker_shutdown_report_marks_telemetry_provider_failure_unhealthy() {
5202+
let telemetry_report = rocketmq_observability::TelemetryShutdownReport {
5203+
subscriber_installed: true,
5204+
file_log_enabled: false,
5205+
dropped_log_lines: 0,
5206+
logs_shutdown_ok: false,
5207+
traces_shutdown_ok: true,
5208+
metrics_shutdown_ok: true,
5209+
logs_shutdown_error: Some("logger provider failed".to_string()),
5210+
traces_shutdown_error: None,
5211+
metrics_shutdown_error: None,
5212+
};
5213+
5214+
let component =
5215+
BrokerShutdownComponentReport::from_telemetry_shutdown_report(&telemetry_report, Duration::from_millis(5));
5216+
5217+
assert!(component.present);
5218+
assert!(!component.healthy);
5219+
assert_eq!(component.name, "observability");
5220+
assert!(component
5221+
.detail
5222+
.expect("telemetry failure detail should be recorded")
5223+
.contains("logger provider failed"));
5224+
}
5225+
51515226
#[tokio::test]
51525227
async fn initial_rpc_hooks_rejects_malformed_inner_client_credentials() {
51535228
let broker_config = Arc::new(BrokerConfig {

rocketmq-controller/src/bin/controller_bootstrap.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,13 @@ async fn run(service_context: ServiceContext) -> Result<()> {
128128
let controller_result = run_controller(config, service_context).await;
129129
let shutdown_result = telemetry_guard
130130
.shutdown()
131+
.into_result()
131132
.context("failed to shutdown controller telemetry bootstrap");
132133

133134
match (controller_result, shutdown_result) {
134135
(Err(error), _) => Err(error),
135136
(Ok(()), Err(error)) => Err(error),
136-
(Ok(()), Ok(())) => Ok(()),
137+
(Ok(()), Ok(_report)) => Ok(()),
137138
}
138139
}
139140

rocketmq-namesrv/src/bin/namesrv_bootstrap_server.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,13 @@ async fn run(service_context: ServiceContext) -> Result<()> {
138138
.map_err(anyhow::Error::from);
139139
let shutdown_result = telemetry_guard
140140
.shutdown()
141+
.into_result()
141142
.context("failed to shutdown namesrv telemetry bootstrap");
142143

143144
match (boot_result, shutdown_result) {
144145
(Err(error), _) => Err(error),
145146
(Ok(()), Err(error)) => Err(error),
146-
(Ok(()), Ok(())) => Ok(()),
147+
(Ok(()), Ok(_report)) => Ok(()),
147148
}
148149
}
149150

rocketmq-observability/src/init.rs

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,48 @@ use crate::config::SubscriberInstallPolicy;
2020
use crate::config::SubscriberInstallStatus;
2121
use crate::error::ObservabilityError;
2222

23+
#[derive(Debug, Clone, PartialEq, Eq)]
24+
pub(crate) struct TelemetryProviderShutdownReport {
25+
pub(crate) logs_shutdown_ok: bool,
26+
pub(crate) traces_shutdown_ok: bool,
27+
pub(crate) metrics_shutdown_ok: bool,
28+
pub(crate) logs_shutdown_error: Option<String>,
29+
pub(crate) traces_shutdown_error: Option<String>,
30+
pub(crate) metrics_shutdown_error: Option<String>,
31+
}
32+
33+
impl Default for TelemetryProviderShutdownReport {
34+
fn default() -> Self {
35+
Self {
36+
logs_shutdown_ok: true,
37+
traces_shutdown_ok: true,
38+
metrics_shutdown_ok: true,
39+
logs_shutdown_error: None,
40+
traces_shutdown_error: None,
41+
metrics_shutdown_error: None,
42+
}
43+
}
44+
}
45+
46+
impl TelemetryProviderShutdownReport {
47+
pub(crate) fn is_healthy(&self) -> bool {
48+
self.logs_shutdown_ok && self.traces_shutdown_ok && self.metrics_shutdown_ok
49+
}
50+
51+
pub(crate) fn into_result(self) -> Result<(), ObservabilityError> {
52+
if let Some(error) = self.logs_shutdown_error {
53+
return Err(ObservabilityError::logs_shutdown(error));
54+
}
55+
if let Some(error) = self.traces_shutdown_error {
56+
return Err(ObservabilityError::traces_shutdown(error));
57+
}
58+
if let Some(error) = self.metrics_shutdown_error {
59+
return Err(ObservabilityError::metrics_shutdown(error));
60+
}
61+
Ok(())
62+
}
63+
}
64+
2365
#[derive(Debug, Default)]
2466
pub struct TelemetryGuard {
2567
subscriber_install_status: SubscriberInstallStatus,
@@ -39,6 +81,10 @@ impl TelemetryGuard {
3981
}
4082

4183
pub fn shutdown(self) -> Result<(), ObservabilityError> {
84+
self.shutdown_with_report().into_result()
85+
}
86+
87+
pub(crate) fn shutdown_with_report(self) -> TelemetryProviderShutdownReport {
4288
self.shutdown_inner()
4389
}
4490

@@ -51,33 +97,44 @@ impl TelemetryGuard {
5197
}
5298

5399
#[cfg(any(feature = "otel-metrics", feature = "otel-traces", feature = "otel-logs"))]
54-
fn shutdown_inner(mut self) -> Result<(), ObservabilityError> {
55-
#[cfg(feature = "prometheus")]
56-
if let Some(prometheus_http) = self.prometheus_http.take() {
57-
prometheus_http.shutdown();
58-
}
100+
fn shutdown_inner(mut self) -> TelemetryProviderShutdownReport {
101+
let mut report = TelemetryProviderShutdownReport::default();
59102

60-
#[cfg(feature = "otel-metrics")]
61-
if let Some(provider) = self.meter_provider.take() {
62-
provider.shutdown().map_err(ObservabilityError::metrics_shutdown)?;
103+
#[cfg(feature = "otel-logs")]
104+
if let Some(provider) = self.logger_provider.take() {
105+
if let Err(error) = provider.shutdown() {
106+
report.logs_shutdown_ok = false;
107+
report.logs_shutdown_error = Some(error.to_string());
108+
}
63109
}
64110

65111
#[cfg(feature = "otel-traces")]
66112
if let Some(provider) = self.tracer_provider.take() {
67-
provider.shutdown().map_err(ObservabilityError::traces_shutdown)?;
113+
if let Err(error) = provider.shutdown() {
114+
report.traces_shutdown_ok = false;
115+
report.traces_shutdown_error = Some(error.to_string());
116+
}
68117
}
69118

70-
#[cfg(feature = "otel-logs")]
71-
if let Some(provider) = self.logger_provider.take() {
72-
provider.shutdown().map_err(ObservabilityError::logs_shutdown)?;
119+
#[cfg(feature = "otel-metrics")]
120+
if let Some(provider) = self.meter_provider.take() {
121+
if let Err(error) = provider.shutdown() {
122+
report.metrics_shutdown_ok = false;
123+
report.metrics_shutdown_error = Some(error.to_string());
124+
}
73125
}
74126

75-
Ok(())
127+
#[cfg(feature = "prometheus")]
128+
if let Some(prometheus_http) = self.prometheus_http.take() {
129+
prometheus_http.shutdown();
130+
}
131+
132+
report
76133
}
77134

78135
#[cfg(not(any(feature = "otel-metrics", feature = "otel-traces", feature = "otel-logs")))]
79-
fn shutdown_inner(self) -> Result<(), ObservabilityError> {
80-
Ok(())
136+
fn shutdown_inner(self) -> TelemetryProviderShutdownReport {
137+
TelemetryProviderShutdownReport::default()
81138
}
82139

83140
#[cfg(feature = "otel-metrics")]

rocketmq-observability/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub use logging::install_global;
4747
pub use logging::FileLogLayer;
4848
pub use logging::LoggingGuard;
4949
pub use logging::TelemetryRuntimeGuard;
50+
pub use logging::TelemetryShutdownReport;
5051

5152
#[cfg(feature = "prometheus")]
5253
#[doc(hidden)]

0 commit comments

Comments
 (0)