public static void log(
            @Nullable Integer sessionId,
            @Nullable Long userId,
            @Nullable DeviceType deviceType,
            @Nullable Integer version,
            String ip,
            long requestId,
            TurmsRequest.KindCase requestType,
            int requestSize,
            long requestTime,
            TurmsNotification response,
            long processingTime) {
        ByteBuf buffer = ByteBufUtil.join(64,
                LOG_FIELD_DELIMITER,
                // session information
                sessionId,
                userId,
                StringUtil.toString(deviceType),
                version,
                ip,
                // request information
                NumberFormatter.toCharBytes(requestId),
                requestType.name(),
                NumberFormatter.toCharBytes(requestSize),
                DateTimeUtil.toBytes(requestTime),
                // response information
                NumberFormatter.toCharBytes(response.getCode()),
                response.hasData()
                        ? response.getData()
                                .getKindCase()
                                .name()
                        : null,
                NumberFormatter.toCharBytes(response.getSerializedSize()),
                NumberFormatter.toCharBytes(processingTime));
        CLIENT_API_LOGGER.info(buffer);
    }
@implNote 1. We don't accept an object like ClientApiLoggingMessage to collect all data to
          avoid creating unnecessary intermediate objects. 2. We use the common log pattern
          (including the trace ID) so that our users don't need to write different parsers
          for them.
turms-gateway/src/main/java/im/turms/gateway/infra/logging/ClientApiLogging.java