diff --git a/src/middlewares/SimApiRequestLogMiddleware.cj b/src/middlewares/SimApiRequestLogMiddleware.cj index 47e4431..2feb55f 100644 --- a/src/middlewares/SimApiRequestLogMiddleware.cj +++ b/src/middlewares/SimApiRequestLogMiddleware.cj @@ -8,14 +8,21 @@ package simapi.middlewares import std.collection.* import std.io.* import std.time.* +import stdx.encoding.json.* import soulsoft_web_http.* import soulsoft_extensions_logging.* import simapi.communications.* import simapi.configurations.* /** - * 请求日志中间件:记录请求方法、URL、请求头、请求体、响应状态码与耗时。 + * 请求日志中间件:记录请求方法、URL、请求头、请求体、响应状态码、耗时与异常。 * 对应 C# 的 SimApi.Middlewares.SimApiRequestLogMiddleware。 + * + * 对齐说明: + * - 请求体按 JSON 字段级截断(对齐 C#:仅对超长字符串字段截断,保留结构) + * - 捕获下游异常并记录,随后重抛(对齐 C# ExceptionDispatchInfo + edi.Throw) + * - 响应体:soulsoft HttpResponse.body 只读不可替换(C# 用 MemoryStream 替换捕获), + * 此处以 Content-Length 作为替代信息;ShowFullResponse 选项因此暂不生效 */ public class SimApiRequestLogMiddleware <: IMiddleware { private let _options: SimApiOptions @@ -51,15 +58,33 @@ public class SimApiRequestLogMiddleware <: IMiddleware { sb.append("*( RequestBody ) =>\n") sb.append(readRequestBody(context)) - // 调用下一级 - next(context) + // 调用下一级,捕获异常以便记录并重抛(对齐 C# ExceptionDispatchInfo) + var exception: ?Exception = None + try { + next(context) + } catch (ex: Exception) { + exception = Some(ex) + } // 响应信息 let elapsed = MonoTime.now() - start let elapsedMs = elapsed / Duration.millisecond - sb.append("*( Response [${context.response.statusCode}] ) => ${elapsedMs}ms\n") + sb.append("*( Response [${context.response.statusCode}] ) => ${elapsedMs}ms") + // 响应体捕获受 soulsoft 限制(body 只读不可替换),记录 Content-Length 作为替代 + if (let Some(len) <- context.response.contentLength) { + sb.append(" (响应体长度: ${len})") + } + sb.append("\n") + if (let Some(ex) <- exception) { + sb.append("Exception: ${ex.toString()}\n") + } _logger.info(sb.toString()) + + // 重抛原异常(对齐 C# edi?.Throw()),由外层 ExceptionMiddleware 处理 + if (let Some(ex) <- exception) { + throw ex + } } private func serializeHeaders(context: HttpContext): String { @@ -107,17 +132,39 @@ public class SimApiRequestLogMiddleware <: IMiddleware { } } + /// 请求体截断:JSON 字段级截断(对齐 C#:仅对超长字符串字段截断),非 JSON 则整串截断 private func truncateBody(body: String): String { - if (_options.simApiRequestLogOptions.requestStringLogLength <= 0 || - body.size <= _options.simApiRequestLogOptions.requestStringLogLength) { + let maxLen = _options.simApiRequestLogOptions.requestStringLogLength + if (maxLen <= 0) { return body + "\n" } - // 简单按长度截断(不做 JSON 字段级截断,保持实现简洁) - let chars = body.toArray() - var sb = StringBuilder() - for (i in 0.._options.simApiRequestLogOptions.requestStringLogLength) { - sb.append(chars[i]) + try { + let jv = JsonValue.fromStr(body) + match (jv.kind()) { + case JsObject => + let obj = jv.asObject() + let newObj = JsonObject() + for ((k, v) in obj.getFields()) { + match (v.kind()) { + case JsString => + let str = v.asString().getValue() + if (str.size > maxLen) { + newObj.put(k, JsonString(str[0..maxLen] + "...(${str.size})")) + } else { + newObj.put(k, v) + } + case _ => newObj.put(k, v) + } + } + return newObj.toString() + "\n" + case _ => () + } + } catch (_: Exception) { } - return "${sb.toString()}...(${body.size})\n" + // 非 JSON 或解析失败:整串按长度截断 + if (body.size <= maxLen) { + return body + "\n" + } + return body[0..maxLen] + "...(${body.size})\n" } }