[Swift] Moya + network log

Retrofit처럼 요청과 응답의 로그를 찍고 싶었다. Moya는 Plugin을 넣을 수 있었다. 전체 코드는 https://gist.github.com/susemi99/841b2c3935b2028b2162842d479de143 에 있다.

MoyaProvider<API>(plugins: [RequestLoggingPlugin()])
import Foundation
import Moya

/// 네트워크 호출 결과 로그 표시
final class RequestLoggingPlugin: PluginType {
  /// API를 보내기 직전에 호출
  func willSend(_ request: RequestType, target: TargetType) {
    guard let httpRequest = request.request else {
      print("--> invalid request")
      return
    }

    let url = httpRequest.description
    let method = httpRequest.httpMethod ?? "unknown method"

    var log = "--> \(method) \(url)\n"
    log.append("API: \(target)\n")

    if let headers = httpRequest.allHTTPHeaderFields, !headers.isEmpty {
      log.append("header: \(headers)\n")
    }

    if let body = httpRequest.httpBody, let bodyString = String(bytes: body, encoding: String.Encoding.utf8) {
      log.append("\(bodyString)\n")
    }

    log.append("--> END \(method)")
    print(log)
  }

  /// API Response
  func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
    switch result {
    case let .success(response):
      onSuceed(response, target: target, isFromError: false)
    case let .failure(error):
      onFail(error, target: target)
    }
  }

  func onSuceed(_ response: Response, target: TargetType, isFromError: Bool) {
    let request = response.request
    let url = request?.url?.absoluteString ?? "nil"
    let statusCode = response.statusCode

    var log = "<-- \(statusCode) \(url)\n"
    log.append("API: \(target)\n")

    response.response?.allHeaderFields.forEach {
      log.append("\($0): \($1)\n")
    }

    if let reString = String(bytes: response.data, encoding: String.Encoding.utf8) {
      log.append("\(reString)\n")
    }

    log.append("<-- END HTTP (\(response.data.count)-byte body)")
    print(log)
  }

  func onFail(_ error: MoyaError, target: TargetType) {
    if let response = error.response {
      onSuceed(response, target: target, isFromError: true)
      return
    }

    var log = "<-- \(error.errorCode) \(target)\n"
    log.append("\(error.failureReason ?? error.errorDescription ?? "unknown error")\n")
    log.append("<-- END HTTP")
    print(log)
  }
}

그러면 이런 식으로 결과가 온다.

--> GET https://api.github.com/users/Moya
API: profile("Moya")
--> END GET
<-- 200 https://api.github.com/users/Moya
API: profile("Moya")
X-Ratelimit-Reset: 1591437524
x-github-media-type: github.v3; format=json
content-security-policy: default-src 'none'
Content-Length: 458
x-xss-protection: 1; mode=block
Cache-Control: public, max-age=60, s-maxage=60
Last-Modified: Fri, 19 Aug 2016 23:35:09 GMT
x-frame-options: deny
Accept-Ranges: bytes
Status: 200 OK
X-GitHub-Request-Id: C5AF:2E51:24D125:2EA4ED:5EDB5AC4
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
Access-Control-Allow-Origin: *
X-Ratelimit-Remaining: 59
Vary: Accept, Accept-Encoding, Accept, X-Requested-With, Accept-Encoding
Etag: W/"d5d249d1ae6fb1c26fccf32a255109f9"
Server: GitHub.com
Date: Sat, 06 Jun 2020 08:58:45 GMT
access-control-expose-headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
X-Ratelimit-Limit: 60
Content-Encoding: gzip
x-content-type-options: nosniff
referrer-policy: origin-when-cross-origin, strict-origin-when-cross-origin
Content-Type: application/json; charset=utf-8
{"login":"Moya","id":13662162,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzNjYyMTYy","avatar_url":"https://avatars3.githubusercontent.com/u/13662162?v=4","gravatar_id":"","url":"https://api.github.com/users/Moya","html_url":"https://github.com/Moya","followers_url":"https://api.github.com/users/Moya/followers","following_url":"https://api.github.com/users/Moya/following{/other_user}","gists_url":"https://api.github.com/users/Moya/gists{/gist_id}","starred_url":"https://api.github.com/users/Moya/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Moya/subscriptions","organizations_url":"https://api.github.com/users/Moya/orgs","repos_url":"https://api.github.com/users/Moya/repos","events_url":"https://api.github.com/users/Moya/events{/privacy}","received_events_url":"https://api.github.com/users/Moya/received_events","type":"Organization","site_admin":false,"name":"Moya","company":null,"blog":"","location":null,"email":null,"hireable":null,"bio":null,"twitter_username":null,"public_repos":6,"public_gists":0,"followers":0,"following":0,"created_at":"2015-08-05T15:03:29Z","updated_at":"2016-08-19T23:35:09Z"}
<-- END HTTP (1133-byte body)

success 가 아니면 이런 식으로 온다.

--> GET https://api.github.com/users/Moya1234
API: profile("Moya1234")
--> END GET
<-- 404 https://api.github.com/users/Moya1234
API: profile("Moya1234")
Date: Sat, 06 Jun 2020 09:00:12 GMT
Server: GitHub.com
x-frame-options: deny
content-security-policy: default-src 'none'
Content-Type: application/json; charset=utf-8
X-Ratelimit-Remaining: 58
x-content-type-options: nosniff
x-github-media-type: github.v3; format=json
X-GitHub-Request-Id: C62D:3EF3:237173:2DB008:5EDB5B1C
x-xss-protection: 1; mode=block
access-control-expose-headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
Vary: Accept-Encoding, Accept, X-Requested-With
X-Ratelimit-Limit: 60
Access-Control-Allow-Origin: *
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
Status: 404 Not Found
Content-Encoding: gzip
X-Ratelimit-Reset: 1591437524
Content-Length: 113
referrer-policy: origin-when-cross-origin, strict-origin-when-cross-origin
{"message":"Not Found","documentation_url":"https://developer.github.com/v3/users/#get-a-single-user"}
<-- END HTTP (102-byte body)