fix(appointment): retain safe synchronization failure diagnostics
This commit is contained in:
+14
-1
@@ -21,7 +21,20 @@ public class AppointmentWorkSynchronizationJob {
|
|||||||
cursor=result.complete()?null:result.nextCursor();
|
cursor=result.complete()?null:result.nextCursor();
|
||||||
} catch(RuntimeException error) {
|
} catch(RuntimeException error) {
|
||||||
cursor=null;
|
cursor=null;
|
||||||
log.warn("Appointment work synchronization incomplete; next scan restarts from first page: {}",error.getClass().getSimpleName());
|
log.warn("Appointment work synchronization incomplete; next scan restarts from first page: {}",failureDiagnostic(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/** Keep causes and code locations, never exception messages that may contain remote payloads or credentials. */
|
||||||
|
static String failureDiagnostic(Throwable error) {
|
||||||
|
var causes=new java.util.ArrayList<String>();
|
||||||
|
var seen=java.util.Collections.newSetFromMap(new java.util.IdentityHashMap<Throwable,Boolean>());
|
||||||
|
for(var current=error;current!=null && causes.size()<6 && seen.add(current);current=current.getCause()) {
|
||||||
|
String status=current instanceof org.springframework.web.server.ResponseStatusException response?"["+response.getStatusCode().value()+"]":"";
|
||||||
|
String location=java.util.Arrays.stream(current.getStackTrace())
|
||||||
|
.filter(frame->frame.getClassName().startsWith("com.baiyue.") && !frame.getClassName().equals(AppointmentWorkPolicy.class.getName()))
|
||||||
|
.findFirst().map(frame->"@"+frame.getClassName()+":"+frame.getLineNumber()).orElse("");
|
||||||
|
causes.add(current.getClass().getSimpleName()+status+location);
|
||||||
|
}
|
||||||
|
return String.join(" <- ",causes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package com.baiyue.lastleg.appointmentwork;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class AppointmentWorkSynchronizationJobTest {
|
||||||
|
@Test void diagnosticKeepsCauseAndCodeWithoutRemoteMessages() {
|
||||||
|
var cause=new IllegalArgumentException("Authorization: Bearer secret; private remote body");
|
||||||
|
var error=new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE,"private response",cause);
|
||||||
|
assertThat(AppointmentWorkSynchronizationJob.failureDiagnostic(error))
|
||||||
|
.contains("ResponseStatusException[503]", "IllegalArgumentException", "AppointmentWorkSynchronizationJobTest:")
|
||||||
|
.doesNotContain("secret", "private", "Authorization", "Bearer");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user