{"action":"created","comment":{"url":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments/194861482","pull_request_review_id":128114966,"id":194861482,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5NDg2MTQ4Mg==","diff_hunk":"@@ -0,0 +1,372 @@\n+#include \"extensions/stat_sinks/hystrix/hystrix.h\"\n+\n+#include <chrono>\n+#include <ctime>\n+#include <iostream>\n+#include <sstream>\n+\n+#include \"common/buffer/buffer_impl.h\"\n+#include \"common/common/logger.h\"\n+#include \"common/http/headers.h\"\n+\n+#include \"absl/strings/str_cat.h\"\n+\n+namespace Envoy {\n+namespace Extensions {\n+namespace StatSinks {\n+namespace Hystrix {\n+\n+const uint64_t HystrixSink::DEFAULT_NUM_OF_BUCKETS;\n+\n+ClusterStatsCache::ClusterStatsCache(const std::string& cluster_name)\n+    : cluster_name_(cluster_name) {}\n+\n+void ClusterStatsCache::printToStream(std::stringstream& out_str) {\n+  const std::string cluster_name_with_prefix = absl::StrCat(cluster_name_, \".\");\n+\n+  printRollingWindow(absl::StrCat(cluster_name_with_prefix, \"errors\"), success_, out_str);\n+  printRollingWindow(absl::StrCat(cluster_name_with_prefix, \"success\"), errors_, out_str);\n+  printRollingWindow(absl::StrCat(cluster_name_with_prefix, \"total\"), timeouts_, out_str);\n+  printRollingWindow(absl::StrCat(cluster_name_with_prefix, \"timeouts\"), rejected_, out_str);\n+  printRollingWindow(absl::StrCat(cluster_name_with_prefix, \"rejected\"), total_, out_str);\n+}\n+\n+void ClusterStatsCache::printRollingWindow(absl::string_view name, RollingWindow rolling_window,\n+                                           std::stringstream& out_str) {\n+  out_str << name << \" | \";\n+  for (auto specific_stat_vec_itr = rolling_window.begin();\n+       specific_stat_vec_itr != rolling_window.end(); ++specific_stat_vec_itr) {\n+    out_str << *specific_stat_vec_itr << \" | \";\n+  }\n+  out_str << std::endl;\n+}\n+\n+// Add new value to rolling window, in place of oldest one.\n+void HystrixSink::pushNewValue(RollingWindow& rolling_window, uint64_t value) {\n+  if (rolling_window.empty()) {\n+    rolling_window.resize(window_size_, value);\n+  } else {\n+    rolling_window[current_index_] = value;\n+  }\n+}\n+\n+uint64_t HystrixSink::getRollingValue(RollingWindow rolling_window) {\n+\n+  if (rolling_window.empty()) {\n+    return 0;\n+  }\n+  // If the counter was reset, the result is negative\n+  // better return 0, will be back to normal once one rolling window passes.\n+  if (rolling_window[current_index_] < rolling_window[(current_index_ + 1) % window_size_]) {\n+    return 0;\n+  } else {\n+    return rolling_window[current_index_] - rolling_window[(current_index_ + 1) % window_size_];\n+  }\n+}\n+\n+void HystrixSink::updateRollingWindowMap(Upstream::ClusterInfoConstSharedPtr cluster_info,\n+                                         ClusterStatsCache& cluster_stats_cache) {\n+  const std::string cluster_name = cluster_info->name();\n+  Upstream::ClusterStats& cluster_stats = cluster_info->stats();\n+  Stats::Scope& cluster_stats_scope = cluster_info->statsScope();\n+\n+  // Combining timeouts+retries - retries are counted  as separate requests\n+  // (alternative: each request including the retries counted as 1).\n+  uint64_t timeouts = cluster_stats.upstream_rq_timeout_.value() +\n+                      cluster_stats.upstream_rq_per_try_timeout_.value();\n+\n+  pushNewValue(cluster_stats_cache.timeouts_, timeouts);\n+\n+  // Combining errors+retry errors - retries are counted as separate requests\n+  // (alternative: each request including the retries counted as 1)\n+  // since timeouts are 504 (or 408), deduce them from here (\"-\" sign).\n+  // Timeout retries were not counted here anyway.\n+  uint64_t errors = cluster_stats_scope.counter(\"upstream_rq_5xx\").value() +\n+                    cluster_stats_scope.counter(\"retry.upstream_rq_5xx\").value() +\n+                    cluster_stats_scope.counter(\"upstream_rq_4xx\").value() +\n+                    cluster_stats_scope.counter(\"retry.upstream_rq_4xx\").value() -\n+                    cluster_stats.upstream_rq_timeout_.value();\n+\n+  pushNewValue(cluster_stats_cache.errors_, errors);\n+\n+  uint64_t success = cluster_stats_scope.counter(\"upstream_rq_2xx\").value();\n+  pushNewValue(cluster_stats_cache.success_, success);\n+\n+  uint64_t rejected = cluster_stats.upstream_rq_pending_overflow_.value();\n+  pushNewValue(cluster_stats_cache.rejected_, rejected);\n+\n+  // should not take from upstream_rq_total since it is updated before its components,\n+  // leading to wrong results such as error percentage higher than 100%\n+  uint64_t total = errors + timeouts + success + rejected;\n+  pushNewValue(cluster_stats_cache.total_, total);\n+\n+  ENVOY_LOG(trace, \"{}\", printRollingWindows());\n+}\n+\n+void HystrixSink::resetRollingWindow() { cluster_stats_cache_map_.clear(); }\n+\n+void HystrixSink::addStringToStream(absl::string_view key, absl::string_view value,\n+                                    std::stringstream& info) {\n+  std::string quoted_value = absl::StrCat(\"\\\"\", value, \"\\\"\");\n+  addInfoToStream(key, quoted_value, info);\n+}\n+\n+void HystrixSink::addIntToStream(absl::string_view key, uint64_t value, std::stringstream& info) {\n+  addInfoToStream(key, std::to_string(value), info);\n+}\n+\n+void HystrixSink::addInfoToStream(absl::string_view key, absl::string_view value,\n+                                  std::stringstream& info) {\n+  if (!info.str().empty()) {\n+    info << \", \";\n+  }\n+  std::string added_info = absl::StrCat(\"\\\"\", key, \"\\\": \", value);\n+  info << added_info;\n+}\n+\n+void HystrixSink::addHystrixCommand(ClusterStatsCache& cluster_stats_cache,\n+                                    absl::string_view cluster_name,\n+                                    uint64_t max_concurrent_requests, uint64_t reporting_hosts,\n+                                    std::chrono::milliseconds rolling_window_ms,\n+                                    std::stringstream& ss) {\n+\n+  std::stringstream cluster_info;\n+  std::time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());\n+\n+  addStringToStream(\"type\", \"HystrixCommand\", cluster_info);\n+  addStringToStream(\"name\", cluster_name, cluster_info);\n+  addStringToStream(\"group\", \"NA\", cluster_info);\n+  addIntToStream(\"currentTime\", static_cast<uint64_t>(currentTime), cluster_info);\n+  addInfoToStream(\"isCircuitBreakerOpen\", \"false\", cluster_info);\n+\n+  uint64_t errors = getRollingValue(cluster_stats_cache.errors_);\n+  uint64_t timeouts = getRollingValue(cluster_stats_cache.timeouts_);\n+  uint64_t rejected = getRollingValue(cluster_stats_cache.rejected_);\n+  uint64_t total = getRollingValue(cluster_stats_cache.total_);\n+\n+  uint64_t error_rate = total == 0 ? 0 : (100 * (errors + timeouts + rejected)) / total;\n+\n+  addIntToStream(\"errorPercentage\", error_rate, cluster_info);\n+  addIntToStream(\"errorCount\", errors, cluster_info);\n+  addIntToStream(\"requestCount\", total, cluster_info);\n+  addIntToStream(\"rollingCountCollapsedRequests\", 0, cluster_info);\n+  addIntToStream(\"rollingCountExceptionsThrown\", 0, cluster_info);\n+  addIntToStream(\"rollingCountFailure\", errors, cluster_info);\n+  addIntToStream(\"rollingCountFallbackFailure\", 0, cluster_info);\n+  addIntToStream(\"rollingCountFallbackRejection\", 0, cluster_info);\n+  addIntToStream(\"rollingCountFallbackSuccess\", 0, cluster_info);\n+  addIntToStream(\"rollingCountResponsesFromCache\", 0, cluster_info);\n+\n+  // Envoy's \"circuit breaker\" has similar meaning to hystrix's isolation\n+  // so we count upstream_rq_pending_overflow and present it as rejected\n+  addIntToStream(\"rollingCountSemaphoreRejected\", rejected, cluster_info);\n+\n+  // Hystrix's short circuit is not similar to Envoy's since it is triggered by 503 responses\n+  // there is no parallel counter in Envoy since as a result of errors (outlier detection)\n+  // requests are not rejected, but rather the node is removed from load balancer healthy pool.\n+  addIntToStream(\"rollingCountShortCircuited\", 0, cluster_info);\n+  addIntToStream(\"rollingCountSuccess\", getRollingValue(cluster_stats_cache.success_),\n+                 cluster_info);\n+  addIntToStream(\"rollingCountThreadPoolRejected\", 0, cluster_info);\n+  addIntToStream(\"rollingCountTimeout\", timeouts, cluster_info);\n+  addIntToStream(\"rollingCountBadRequests\", 0, cluster_info);\n+  addIntToStream(\"currentConcurrentExecutionCount\", 0, cluster_info);\n+  addIntToStream(\"latencyExecute_mean\", 0, cluster_info);\n+\n+  // TODO trabetti : add histogram information once available by PR #2932\n+  addInfoToStream(\n+      \"latencyExecute\",\n+      \"{\\\"0\\\":0,\\\"25\\\":0,\\\"50\\\":0,\\\"75\\\":0,\\\"90\\\":0,\\\"95\\\":0,\\\"99\\\":0,\\\"99.5\\\":0,\\\"100\\\":0}\",\n+      cluster_info);\n+  addIntToStream(\"propertyValue_circuitBreakerRequestVolumeThreshold\", 0, cluster_info);\n+  addIntToStream(\"propertyValue_circuitBreakerSleepWindowInMilliseconds\", 0, cluster_info);\n+  addIntToStream(\"propertyValue_circuitBreakerErrorThresholdPercentage\", 0, cluster_info);\n+  addInfoToStream(\"propertyValue_circuitBreakerForceOpen\", \"false\", cluster_info);\n+  addInfoToStream(\"propertyValue_circuitBreakerForceClosed\", \"true\", cluster_info);\n+  addStringToStream(\"propertyValue_executionIsolationStrategy\", \"SEMAPHORE\", cluster_info);\n+  addIntToStream(\"propertyValue_executionIsolationThreadTimeoutInMilliseconds\", 0, cluster_info);\n+  addInfoToStream(\"propertyValue_executionIsolationThreadInterruptOnTimeout\", \"false\",\n+                  cluster_info);\n+  addIntToStream(\"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests\",\n+                 max_concurrent_requests, cluster_info);\n+  addIntToStream(\"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests\", 0, cluster_info);\n+  addInfoToStream(\"propertyValue_requestCacheEnabled\", \"false\", cluster_info);\n+  addInfoToStream(\"propertyValue_requestLogEnabled\", \"true\", cluster_info);\n+  addIntToStream(\"reportingHosts\", reporting_hosts, cluster_info);\n+  addIntToStream(\"propertyValue_metricsRollingStatisticalWindowInMilliseconds\",\n+                 rolling_window_ms.count(), cluster_info);\n+\n+  ss << \"data: {\" << cluster_info.str() << \"}\" << std::endl << std::endl;\n+}\n+\n+void HystrixSink::addHystrixThreadPool(absl::string_view cluster_name, uint64_t queue_size,\n+                                       uint64_t reporting_hosts,\n+                                       std::chrono::milliseconds rolling_window_ms,\n+                                       std::stringstream& ss) {\n+  std::stringstream cluster_info;\n+\n+  addIntToStream(\"currentPoolSize\", 0, cluster_info);\n+  addIntToStream(\"rollingMaxActiveThreads\", 0, cluster_info);\n+  addIntToStream(\"currentActiveCount\", 0, cluster_info);\n+  addIntToStream(\"currentCompletedTaskCount\", 0, cluster_info);\n+  addIntToStream(\"propertyValue_queueSizeRejectionThreshold\", queue_size, cluster_info);\n+  addStringToStream(\"type\", \"HystrixThreadPool\", cluster_info);\n+  addIntToStream(\"reportingHosts\", reporting_hosts, cluster_info);\n+  addIntToStream(\"propertyValue_metricsRollingStatisticalWindowInMilliseconds\",\n+                 rolling_window_ms.count(), cluster_info);\n+  addStringToStream(\"name\", cluster_name, cluster_info);\n+  addIntToStream(\"currentLargestPoolSize\", 0, cluster_info);\n+  addIntToStream(\"currentCorePoolSize\", 0, cluster_info);\n+  addIntToStream(\"currentQueueSize\", 0, cluster_info);\n+  addIntToStream(\"currentTaskCount\", 0, cluster_info);\n+  addIntToStream(\"rollingCountThreadsExecuted\", 0, cluster_info);\n+  addIntToStream(\"currentMaximumPoolSize\", 0, cluster_info);\n+\n+  ss << \"data: {\" << cluster_info.str() << \"}\" << std::endl << std::endl;\n+}\n+\n+void HystrixSink::addClusterStatsToStream(ClusterStatsCache& cluster_stats_cache,\n+                                          absl::string_view cluster_name,\n+                                          uint64_t max_concurrent_requests,\n+                                          uint64_t reporting_hosts,\n+                                          std::chrono::milliseconds rolling_window_ms,","path":"source/extensions/stat_sinks/hystrix/hystrix.cc","position":230,"original_position":232,"commit_id":"d743fb1572a4f9e7f5dec4521f9becc92fd40c4a","original_commit_id":"d464f8c1f9de8eb21775ee1768006008543a21c6","user":{"login":"mrice32","id":11791551,"node_id":"MDQ6VXNlcjExNzkxNTUx","avatar_url":"https://avatars3.githubusercontent.com/u/11791551?v=4","gravatar_id":"","url":"https://api.github.com/users/mrice32","html_url":"https://github.com/mrice32","followers_url":"https://api.github.com/users/mrice32/followers","following_url":"https://api.github.com/users/mrice32/following{/other_user}","gists_url":"https://api.github.com/users/mrice32/gists{/gist_id}","starred_url":"https://api.github.com/users/mrice32/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrice32/subscriptions","organizations_url":"https://api.github.com/users/mrice32/orgs","repos_url":"https://api.github.com/users/mrice32/repos","events_url":"https://api.github.com/users/mrice32/events{/privacy}","received_events_url":"https://api.github.com/users/mrice32/received_events","type":"User","site_admin":false},"body":"Okay, sounds good.  Not a huge deal to me either way.","created_at":"2018-06-12T19:29:49Z","updated_at":"2018-06-12T19:29:49Z","html_url":"https://github.com/envoyproxy/envoy/pull/3425#discussion_r194861482","pull_request_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments/194861482"},"html":{"href":"https://github.com/envoyproxy/envoy/pull/3425#discussion_r194861482"},"pull_request":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425"}},"in_reply_to_id":194525587},"pull_request":{"url":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425","id":188837699,"node_id":"MDExOlB1bGxSZXF1ZXN0MTg4ODM3Njk5","html_url":"https://github.com/envoyproxy/envoy/pull/3425","diff_url":"https://github.com/envoyproxy/envoy/pull/3425.diff","patch_url":"https://github.com/envoyproxy/envoy/pull/3425.patch","issue_url":"https://api.github.com/repos/envoyproxy/envoy/issues/3425","number":3425,"state":"open","locked":false,"title":"support for hystrix dashboard through an admin endpoint, using sink","user":{"login":"trabetti","id":31645500,"node_id":"MDQ6VXNlcjMxNjQ1NTAw","avatar_url":"https://avatars0.githubusercontent.com/u/31645500?v=4","gravatar_id":"","url":"https://api.github.com/users/trabetti","html_url":"https://github.com/trabetti","followers_url":"https://api.github.com/users/trabetti/followers","following_url":"https://api.github.com/users/trabetti/following{/other_user}","gists_url":"https://api.github.com/users/trabetti/gists{/gist_id}","starred_url":"https://api.github.com/users/trabetti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trabetti/subscriptions","organizations_url":"https://api.github.com/users/trabetti/orgs","repos_url":"https://api.github.com/users/trabetti/repos","events_url":"https://api.github.com/users/trabetti/events{/privacy}","received_events_url":"https://api.github.com/users/trabetti/received_events","type":"User","site_admin":false},"body":"Signed-off-by: talis talis@il.ibm.com\r\n\r\nadmin filter: add support for hystrix dashboard output\r\n\r\nDescription:\r\nThis PR adds an admin endoint for hystrix dashboard output.\r\nWhen there is a request to admin_port/hystrix_event_stream, envoy starts sending SSE stream, using the same socket of the initial request.\r\nThe admin URL can be accessed directly from hystrix dashboard.\r\n\r\nRisk Level: Medium\r\n\r\nTesting:\r\nAdded unit testing hystrix_test.cc - WIP\r\nThe admin endpoint was not tested, since it returns an infinite event stream, so current mechanisms for testing admin endpoint do not work. If there are any suggestions on how to test it, we will add.\r\n\r\nDocs Changes:\r\n\r\nRelease Notes:\r\nSupport was added to hystrix event stream output. From hystrix dashboard, set the Eureka URL to envoy_ip:8001, and add stream from http://envoy_ip:8001/hystrix_event_stream\r\nNote that not all hystrix dashboard statistics are supported. More detail can be found here: <link to the documentation generated from docs/root/operations/admin.rst >\r\n\r\nFixes #1244\r\nsplit from PR #2736","created_at":"2018-05-17T19:27:13Z","updated_at":"2018-06-12T19:29:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2f810ae64c765ef480b69995398bafea99fce63d","assignee":{"login":"jmarantz","id":1942589,"node_id":"MDQ6VXNlcjE5NDI1ODk=","avatar_url":"https://avatars3.githubusercontent.com/u/1942589?v=4","gravatar_id":"","url":"https://api.github.com/users/jmarantz","html_url":"https://github.com/jmarantz","followers_url":"https://api.github.com/users/jmarantz/followers","following_url":"https://api.github.com/users/jmarantz/following{/other_user}","gists_url":"https://api.github.com/users/jmarantz/gists{/gist_id}","starred_url":"https://api.github.com/users/jmarantz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmarantz/subscriptions","organizations_url":"https://api.github.com/users/jmarantz/orgs","repos_url":"https://api.github.com/users/jmarantz/repos","events_url":"https://api.github.com/users/jmarantz/events{/privacy}","received_events_url":"https://api.github.com/users/jmarantz/received_events","type":"User","site_admin":false},"assignees":[{"login":"jmarantz","id":1942589,"node_id":"MDQ6VXNlcjE5NDI1ODk=","avatar_url":"https://avatars3.githubusercontent.com/u/1942589?v=4","gravatar_id":"","url":"https://api.github.com/users/jmarantz","html_url":"https://github.com/jmarantz","followers_url":"https://api.github.com/users/jmarantz/followers","following_url":"https://api.github.com/users/jmarantz/following{/other_user}","gists_url":"https://api.github.com/users/jmarantz/gists{/gist_id}","starred_url":"https://api.github.com/users/jmarantz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmarantz/subscriptions","organizations_url":"https://api.github.com/users/jmarantz/orgs","repos_url":"https://api.github.com/users/jmarantz/repos","events_url":"https://api.github.com/users/jmarantz/events{/privacy}","received_events_url":"https://api.github.com/users/jmarantz/received_events","type":"User","site_admin":false},{"login":"mrice32","id":11791551,"node_id":"MDQ6VXNlcjExNzkxNTUx","avatar_url":"https://avatars3.githubusercontent.com/u/11791551?v=4","gravatar_id":"","url":"https://api.github.com/users/mrice32","html_url":"https://github.com/mrice32","followers_url":"https://api.github.com/users/mrice32/followers","following_url":"https://api.github.com/users/mrice32/following{/other_user}","gists_url":"https://api.github.com/users/mrice32/gists{/gist_id}","starred_url":"https://api.github.com/users/mrice32/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrice32/subscriptions","organizations_url":"https://api.github.com/users/mrice32/orgs","repos_url":"https://api.github.com/users/mrice32/repos","events_url":"https://api.github.com/users/mrice32/events{/privacy}","received_events_url":"https://api.github.com/users/mrice32/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425/commits","review_comments_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425/comments","review_comment_url":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/envoyproxy/envoy/issues/3425/comments","statuses_url":"https://api.github.com/repos/envoyproxy/envoy/statuses/d743fb1572a4f9e7f5dec4521f9becc92fd40c4a","head":{"label":"trabetti:hystrix_dashboard_support","ref":"hystrix_dashboard_support","sha":"d743fb1572a4f9e7f5dec4521f9becc92fd40c4a","user":{"login":"trabetti","id":31645500,"node_id":"MDQ6VXNlcjMxNjQ1NTAw","avatar_url":"https://avatars0.githubusercontent.com/u/31645500?v=4","gravatar_id":"","url":"https://api.github.com/users/trabetti","html_url":"https://github.com/trabetti","followers_url":"https://api.github.com/users/trabetti/followers","following_url":"https://api.github.com/users/trabetti/following{/other_user}","gists_url":"https://api.github.com/users/trabetti/gists{/gist_id}","starred_url":"https://api.github.com/users/trabetti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trabetti/subscriptions","organizations_url":"https://api.github.com/users/trabetti/orgs","repos_url":"https://api.github.com/users/trabetti/repos","events_url":"https://api.github.com/users/trabetti/events{/privacy}","received_events_url":"https://api.github.com/users/trabetti/received_events","type":"User","site_admin":false},"repo":{"id":108457854,"node_id":"MDEwOlJlcG9zaXRvcnkxMDg0NTc4NTQ=","name":"envoy","full_name":"trabetti/envoy","owner":{"login":"trabetti","id":31645500,"node_id":"MDQ6VXNlcjMxNjQ1NTAw","avatar_url":"https://avatars0.githubusercontent.com/u/31645500?v=4","gravatar_id":"","url":"https://api.github.com/users/trabetti","html_url":"https://github.com/trabetti","followers_url":"https://api.github.com/users/trabetti/followers","following_url":"https://api.github.com/users/trabetti/following{/other_user}","gists_url":"https://api.github.com/users/trabetti/gists{/gist_id}","starred_url":"https://api.github.com/users/trabetti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trabetti/subscriptions","organizations_url":"https://api.github.com/users/trabetti/orgs","repos_url":"https://api.github.com/users/trabetti/repos","events_url":"https://api.github.com/users/trabetti/events{/privacy}","received_events_url":"https://api.github.com/users/trabetti/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/trabetti/envoy","description":"C++ front/service proxy","fork":true,"url":"https://api.github.com/repos/trabetti/envoy","forks_url":"https://api.github.com/repos/trabetti/envoy/forks","keys_url":"https://api.github.com/repos/trabetti/envoy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/trabetti/envoy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/trabetti/envoy/teams","hooks_url":"https://api.github.com/repos/trabetti/envoy/hooks","issue_events_url":"https://api.github.com/repos/trabetti/envoy/issues/events{/number}","events_url":"https://api.github.com/repos/trabetti/envoy/events","assignees_url":"https://api.github.com/repos/trabetti/envoy/assignees{/user}","branches_url":"https://api.github.com/repos/trabetti/envoy/branches{/branch}","tags_url":"https://api.github.com/repos/trabetti/envoy/tags","blobs_url":"https://api.github.com/repos/trabetti/envoy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/trabetti/envoy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/trabetti/envoy/git/refs{/sha}","trees_url":"https://api.github.com/repos/trabetti/envoy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/trabetti/envoy/statuses/{sha}","languages_url":"https://api.github.com/repos/trabetti/envoy/languages","stargazers_url":"https://api.github.com/repos/trabetti/envoy/stargazers","contributors_url":"https://api.github.com/repos/trabetti/envoy/contributors","subscribers_url":"https://api.github.com/repos/trabetti/envoy/subscribers","subscription_url":"https://api.github.com/repos/trabetti/envoy/subscription","commits_url":"https://api.github.com/repos/trabetti/envoy/commits{/sha}","git_commits_url":"https://api.github.com/repos/trabetti/envoy/git/commits{/sha}","comments_url":"https://api.github.com/repos/trabetti/envoy/comments{/number}","issue_comment_url":"https://api.github.com/repos/trabetti/envoy/issues/comments{/number}","contents_url":"https://api.github.com/repos/trabetti/envoy/contents/{+path}","compare_url":"https://api.github.com/repos/trabetti/envoy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/trabetti/envoy/merges","archive_url":"https://api.github.com/repos/trabetti/envoy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/trabetti/envoy/downloads","issues_url":"https://api.github.com/repos/trabetti/envoy/issues{/number}","pulls_url":"https://api.github.com/repos/trabetti/envoy/pulls{/number}","milestones_url":"https://api.github.com/repos/trabetti/envoy/milestones{/number}","notifications_url":"https://api.github.com/repos/trabetti/envoy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/trabetti/envoy/labels{/name}","releases_url":"https://api.github.com/repos/trabetti/envoy/releases{/id}","deployments_url":"https://api.github.com/repos/trabetti/envoy/deployments","created_at":"2017-10-26T19:43:33Z","updated_at":"2017-10-26T19:43:38Z","pushed_at":"2018-06-11T21:03:14Z","git_url":"git://github.com/trabetti/envoy.git","ssh_url":"git@github.com:trabetti/envoy.git","clone_url":"https://github.com/trabetti/envoy.git","svn_url":"https://github.com/trabetti/envoy","homepage":"https://envoyproxy.github.io/","size":15793,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"envoyproxy:master","ref":"master","sha":"554e57a89e95a9d9bed684e5d663d217a1ea502c","user":{"login":"envoyproxy","id":30125649,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMwMTI1NjQ5","avatar_url":"https://avatars1.githubusercontent.com/u/30125649?v=4","gravatar_id":"","url":"https://api.github.com/users/envoyproxy","html_url":"https://github.com/envoyproxy","followers_url":"https://api.github.com/users/envoyproxy/followers","following_url":"https://api.github.com/users/envoyproxy/following{/other_user}","gists_url":"https://api.github.com/users/envoyproxy/gists{/gist_id}","starred_url":"https://api.github.com/users/envoyproxy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/envoyproxy/subscriptions","organizations_url":"https://api.github.com/users/envoyproxy/orgs","repos_url":"https://api.github.com/users/envoyproxy/repos","events_url":"https://api.github.com/users/envoyproxy/events{/privacy}","received_events_url":"https://api.github.com/users/envoyproxy/received_events","type":"Organization","site_admin":false},"repo":{"id":65214191,"node_id":"MDEwOlJlcG9zaXRvcnk2NTIxNDE5MQ==","name":"envoy","full_name":"envoyproxy/envoy","owner":{"login":"envoyproxy","id":30125649,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMwMTI1NjQ5","avatar_url":"https://avatars1.githubusercontent.com/u/30125649?v=4","gravatar_id":"","url":"https://api.github.com/users/envoyproxy","html_url":"https://github.com/envoyproxy","followers_url":"https://api.github.com/users/envoyproxy/followers","following_url":"https://api.github.com/users/envoyproxy/following{/other_user}","gists_url":"https://api.github.com/users/envoyproxy/gists{/gist_id}","starred_url":"https://api.github.com/users/envoyproxy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/envoyproxy/subscriptions","organizations_url":"https://api.github.com/users/envoyproxy/orgs","repos_url":"https://api.github.com/users/envoyproxy/repos","events_url":"https://api.github.com/users/envoyproxy/events{/privacy}","received_events_url":"https://api.github.com/users/envoyproxy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/envoyproxy/envoy","description":"C++ front/service proxy","fork":false,"url":"https://api.github.com/repos/envoyproxy/envoy","forks_url":"https://api.github.com/repos/envoyproxy/envoy/forks","keys_url":"https://api.github.com/repos/envoyproxy/envoy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/envoyproxy/envoy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/envoyproxy/envoy/teams","hooks_url":"https://api.github.com/repos/envoyproxy/envoy/hooks","issue_events_url":"https://api.github.com/repos/envoyproxy/envoy/issues/events{/number}","events_url":"https://api.github.com/repos/envoyproxy/envoy/events","assignees_url":"https://api.github.com/repos/envoyproxy/envoy/assignees{/user}","branches_url":"https://api.github.com/repos/envoyproxy/envoy/branches{/branch}","tags_url":"https://api.github.com/repos/envoyproxy/envoy/tags","blobs_url":"https://api.github.com/repos/envoyproxy/envoy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/envoyproxy/envoy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/envoyproxy/envoy/git/refs{/sha}","trees_url":"https://api.github.com/repos/envoyproxy/envoy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/envoyproxy/envoy/statuses/{sha}","languages_url":"https://api.github.com/repos/envoyproxy/envoy/languages","stargazers_url":"https://api.github.com/repos/envoyproxy/envoy/stargazers","contributors_url":"https://api.github.com/repos/envoyproxy/envoy/contributors","subscribers_url":"https://api.github.com/repos/envoyproxy/envoy/subscribers","subscription_url":"https://api.github.com/repos/envoyproxy/envoy/subscription","commits_url":"https://api.github.com/repos/envoyproxy/envoy/commits{/sha}","git_commits_url":"https://api.github.com/repos/envoyproxy/envoy/git/commits{/sha}","comments_url":"https://api.github.com/repos/envoyproxy/envoy/comments{/number}","issue_comment_url":"https://api.github.com/repos/envoyproxy/envoy/issues/comments{/number}","contents_url":"https://api.github.com/repos/envoyproxy/envoy/contents/{+path}","compare_url":"https://api.github.com/repos/envoyproxy/envoy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/envoyproxy/envoy/merges","archive_url":"https://api.github.com/repos/envoyproxy/envoy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/envoyproxy/envoy/downloads","issues_url":"https://api.github.com/repos/envoyproxy/envoy/issues{/number}","pulls_url":"https://api.github.com/repos/envoyproxy/envoy/pulls{/number}","milestones_url":"https://api.github.com/repos/envoyproxy/envoy/milestones{/number}","notifications_url":"https://api.github.com/repos/envoyproxy/envoy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/envoyproxy/envoy/labels{/name}","releases_url":"https://api.github.com/repos/envoyproxy/envoy/releases{/id}","deployments_url":"https://api.github.com/repos/envoyproxy/envoy/deployments","created_at":"2016-08-08T15:07:24Z","updated_at":"2018-06-12T18:49:56Z","pushed_at":"2018-06-12T19:14:50Z","git_url":"git://github.com/envoyproxy/envoy.git","ssh_url":"git@github.com:envoyproxy/envoy.git","clone_url":"https://github.com/envoyproxy/envoy.git","svn_url":"https://github.com/envoyproxy/envoy","homepage":"https://www.envoyproxy.io","size":16254,"stargazers_count":5203,"watchers_count":5203,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":669,"mirror_url":null,"archived":false,"open_issues_count":309,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":669,"open_issues":309,"watchers":5203,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425"},"html":{"href":"https://github.com/envoyproxy/envoy/pull/3425"},"issue":{"href":"https://api.github.com/repos/envoyproxy/envoy/issues/3425"},"comments":{"href":"https://api.github.com/repos/envoyproxy/envoy/issues/3425/comments"},"review_comments":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425/comments"},"review_comment":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/envoyproxy/envoy/pulls/3425/commits"},"statuses":{"href":"https://api.github.com/repos/envoyproxy/envoy/statuses/d743fb1572a4f9e7f5dec4521f9becc92fd40c4a"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"mrice32"}}