@Override
    public String formatResponse(RequestLog log) {
        requireNonNull(log, "log");

        final int flags = log.availabilityStamp();
        final RequestContext ctx = log.context();
        if (!RequestLogProperty.RESPONSE_START_TIME.isAvailable(flags)) {
            if (includeContext) {
                return ctx + " Response: {}";
            } else {
                return "Response: {}";
            }
        }

        String responseCauseString = null;
        if (RequestLogProperty.RESPONSE_CAUSE.isAvailable(flags)) {
            final Throwable cause = log.responseCause();
            if (cause != null) {
                responseCauseString = cause.toString();
            }
        }

        final String sanitizedHeaders;
        if (RequestLogProperty.RESPONSE_HEADERS.isAvailable(flags)) {
            sanitizedHeaders = responseHeadersSanitizer.apply(ctx, log.responseHeaders());
        } else {
            sanitizedHeaders = null;
        }

        String sanitizedPreview = null;
        if (RequestLogProperty.RESPONSE_CONTENT_PREVIEW.isAvailable(flags)) {
            final String contentPreview = log.responseContentPreview();
            if (contentPreview != null) {
                sanitizedPreview = responseContentSanitizer.apply(ctx, contentPreview);
            }
        }
        String sanitizedContent = null;
        if (RequestLogProperty.RESPONSE_CONTENT.isAvailable(flags)) {
            final Object content = log.responseContent();
            if (content != null) {
                sanitizedContent = responseContentSanitizer.apply(ctx, content);
            }
        }

        final String sanitizedTrailers;
        if (RequestLogProperty.RESPONSE_TRAILERS.isAvailable(flags) &&
            !log.responseTrailers().isEmpty()) {
            sanitizedTrailers = responseTrailersSanitizer.apply(ctx, log.responseTrailers());
        } else {
            sanitizedTrailers = null;
        }

        final String ctxString;
        if (includeContext) {
            // ctx internally uses TemporaryThreadLocals, so we should call ctx.toString() outside of acquire().
            ctxString = ctx.toString() + ' ';
        } else {
            ctxString = null;
        }

        try (TemporaryThreadLocals tempThreadLocals = TemporaryThreadLocals.acquire()) {
            final StringBuilder buf = tempThreadLocals.stringBuilder();
            if (ctxString != null) {
                buf.append(ctxString);
            }
            buf.append("Response: {startTime=");
            TextFormatter.appendEpochMicros(buf, log.responseStartTimeMicros());

            if (RequestLogProperty.RESPONSE_LENGTH.isAvailable(flags)) {
                buf.append(", length=");
                TextFormatter.appendSize(buf, log.responseLength());
            }

            if (RequestLogProperty.RESPONSE_END_TIME.isAvailable(flags)) {
                buf.append(", duration=");
                TextFormatter.appendElapsed(buf, log.responseDurationNanos());
                buf.append(", totalDuration=");
                TextFormatter.appendElapsed(buf, log.totalDurationNanos());
            }

            if (responseCauseString != null) {
                buf.append(", cause=").append(responseCauseString);
            }

            if (sanitizedHeaders != null) {
                buf.append(", headers=").append(sanitizedHeaders);
            }

            if (sanitizedPreview != null) {
                buf.append(", preview=").append(sanitizedPreview);
            }

            if (sanitizedContent != null) {
                buf.append(", content=").append(sanitizedContent);
            }

            if (sanitizedTrailers != null) {
                buf.append(", trailers=").append(sanitizedTrailers);
            }
            buf.append('}');

            final List<RequestLogAccess> children = log.children();
            final int numChildren = children.size();
            if (numChildren > 1) {
                // Append only when there were retries which the numChildren is greater than 1.
                buf.append(", {totalAttempts=");
                buf.append(numChildren);
                buf.append('}');
            }

            return buf.toString();
        }
    }
A formatter that converts {@link RequestOnlyLog} or {@link RequestLog} into text message.
core/src/main/java/com/linecorp/armeria/common/logging/TextLogFormatter.java