1010import lombok .Getter ;
1111import lombok .extern .slf4j .Slf4j ;
1212import okhttp3 .*;
13+ import okhttp3 .extension .logging .HttpLogLevel ;
1314
1415import java .io .IOException ;
1516import java .util .Map ;
@@ -82,11 +83,11 @@ private OpenClawHttpClient(OpenClawHttpClientConfig config, ObjectMapper objectM
8283 this .objectMapper = objectMapper != null ? objectMapper : createObjectMapper ();
8384 this .httpClient = Objects .requireNonNull (httpClient , "httpClient" );
8485 this .ownsHttpClient = ownsHttpClient ;
85- debug ("OpenClaw HTTP client initialized: baseUrl={}, connectTimeoutMs={}, readTimeoutMs={}, "
86- + "callTimeoutMs={}, retryOnConnectionFailure={}, detailedLoggingEnabled ={}" ,
86+ debug (HttpLogLevel . BASIC , "OpenClaw HTTP client initialized: baseUrl={}, connectTimeoutMs={}, readTimeoutMs={}, "
87+ + "callTimeoutMs={}, retryOnConnectionFailure={}, debugLevel ={}" ,
8788 config .getBaseUrl (), config .getConnectTimeoutMillis (), config .getReadTimeoutMillis (),
8889 config .getCallTimeoutMillis (), config .isRetryOnConnectionFailure (),
89- config .isDetailedLoggingEnabled ());
90+ config .getDebug (). getLevel ());
9091 }
9192
9293 /**
@@ -138,9 +139,7 @@ protected Request.Builder authedBuilder(String url, Map<String, String> headers)
138139 headers .forEach ((k , v ) -> {
139140 if (k != null && v != null ) {
140141 builder .header (k , v );
141- if (config .isDetailedLoggingEnabled ()) {
142- debug ("Added header: {}={}" , k , redactHeader (k , v ));
143- }
142+ debug (HttpLogLevel .HEADERS , "Added header: {}={}" , k , redactHeader (k , v ));
144143 }
145144 });
146145 }
@@ -201,9 +200,7 @@ protected CompletableFuture<String> postJsonAsync(String path, Object body, Map<
201200
202201 try {
203202 String json = objectMapper .writeValueAsString (body );
204- if (config .isDetailedLoggingEnabled ()) {
205- debug ("Request body: {}" , truncate (json ));
206- }
203+ debug (HttpLogLevel .BODY , "Request body: {}" , truncate (json ));
207204
208205 Request request = authedBuilder (url , headers )
209206 .post (RequestBody .create (json , JSON ))
@@ -284,10 +281,10 @@ protected CompletableFuture<String> executeAsync(Request request, String url,
284281 HttpCallCancellation cancellation ) {
285282 long requestId = REQUEST_SEQUENCE .incrementAndGet ();
286283 long startedAt = System .nanoTime ();
287- debug ("HTTP request started: requestId={}, method={}, url={}" , requestId , request . method (), request . url ());
288- if ( config . isDetailedLoggingEnabled ()) {
289- debug ("HTTP request details : requestId={}, headers={}" , requestId , redactHeaders ( request . headers ()));
290- }
284+ debug (HttpLogLevel . BASIC , "HTTP request started: requestId={}, method={}, url={}" ,
285+ requestId , request . method (), request . url ());
286+ debug (HttpLogLevel . HEADERS , "HTTP request headers : requestId={}, headers={}" ,
287+ requestId , redactHeaders ( request . headers ()));
291288
292289 // 传输层只读取状态码和响应体;此处统一把非 2xx 响应转换为携带诊断信息的 SDK 异常。
293290 CompletableFuture <String > result = executeResponseAsync (request , cancellation ).thenApply (response -> {
@@ -303,11 +300,9 @@ protected CompletableFuture<String> executeAsync(Request request, String url,
303300 requestId , request .method (), request .url (), elapsedMillis (startedAt ), unwrap (error ).getMessage ());
304301 return ;
305302 }
306- debug ("HTTP request completed: requestId={}, method={}, url={}, bodyLength={}, elapsedMs={}" ,
303+ debug (HttpLogLevel . BASIC , "HTTP request completed: requestId={}, method={}, url={}, bodyLength={}, elapsedMs={}" ,
307304 requestId , request .method (), request .url (), respBody .length (), elapsedMillis (startedAt ));
308- if (config .isDetailedLoggingEnabled ()) {
309- debug ("HTTP response body: requestId={}, body={}" , requestId , truncate (respBody ));
310- }
305+ debug (HttpLogLevel .BODY , "HTTP response body: requestId={}, body={}" , requestId , truncate (respBody ));
311306 });
312307 }
313308
@@ -450,11 +445,11 @@ private long elapsedMillis(long startedAt) {
450445 return (System .nanoTime () - startedAt ) / 1_000_000L ;
451446 }
452447
453- private String truncate (String value ) {
448+ protected String truncate (String value ) {
454449 if (Objects .isNull (value )) {
455450 return "" ;
456451 }
457- int limit = Math . max ( 0 , config .getMaxLoggedBodyLength () );
452+ int limit = config .getDebug (). resolveMaxContentLength ( );
458453 return value .length () <= limit ? value : value .substring (0 , limit ) + "...<truncated>" ;
459454 }
460455
@@ -469,7 +464,7 @@ private Headers redactHeaders(Headers headers) {
469464 private String redactHeader (String name , String value ) {
470465 if ("authorization" .equalsIgnoreCase (name ) || name .toLowerCase ().contains ("token" )
471466 || name .toLowerCase ().contains ("key" )) {
472- return "██ " ;
467+ return "<redacted> " ;
473468 }
474469 return value ;
475470 }
@@ -567,7 +562,18 @@ public CompletableFuture<Void> healthAsync() {
567562 * @param args 填充消息模板占位符的参数
568563 */
569564 protected void debug (String msg , Object ... args ) {
570- if (log .isDebugEnabled ()) {
565+ debug (HttpLogLevel .BASIC , msg , args );
566+ }
567+
568+ /**
569+ * 在统一调试配置允许指定级别且 SLF4J 开启 DEBUG 时记录日志。
570+ *
571+ * @param level 信息要求的最低 HTTP 日志级别
572+ * @param msg 包含 SLF4J 占位符的日志消息模板
573+ * @param args 填充消息模板占位符的参数
574+ */
575+ protected void debug (HttpLogLevel level , String msg , Object ... args ) {
576+ if (config .getDebug ().allows (level ) && log .isDebugEnabled ()) {
571577 log .debug (msg , args );
572578 }
573579 }
0 commit comments