@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)) {
            return "{\"type\": \"response\"}";
        }

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

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

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

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

            final ObjectNode objectNode = objectMapper.createObjectNode();
            objectNode.put("type", "response");
            objectNode.put("startTime",
                           TextFormatter.epochMicros(log.responseStartTimeMicros()).toString());

            if (RequestLogProperty.RESPONSE_LENGTH.isAvailable(flags)) {
                objectNode.put("length", TextFormatter.size(log.responseLength()).toString());
            }

            if (RequestLogProperty.RESPONSE_END_TIME.isAvailable(flags)) {
                objectNode.put("duration", TextFormatter.elapsed(log.responseDurationNanos()).toString());
                objectNode.put("totalDuration",
                               TextFormatter.elapsed(log.totalDurationNanos()).toString());
            }

            if (responseCauseString != null) {
                objectNode.put("cause", responseCauseString);
            }

            if (sanitizedHeaders != null) {
                objectNode.set("headers", sanitizedHeaders);
            }

            if (sanitizedPreview != null) {
                objectNode.set("preview", sanitizedPreview);
            }

            if (sanitizedContent != null) {
                objectNode.set("content", sanitizedContent);
            }

            if (sanitizedTrailers != null) {
                objectNode.set("trailers", sanitizedTrailers);
            }

            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.
                objectNode.put("totalAttempts", String.valueOf(numChildren));
            }
            return objectMapper.writeValueAsString(objectNode);
        } catch (Exception e) {
            logger.warn("Unexpected exception while formatting a response log: {}", log, e);
            return "{}";
        }
    }
A formatter that converts a {@link RequestOnlyLog} or {@link RequestLog} into a JSON format message.
core/src/main/java/com/linecorp/armeria/common/logging/JsonLogFormatter.java