{"action":"created","comment":{"url":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/comments/60663759","id":60663759,"diff_hunk":"@@ -0,0 +1,257 @@\n+package com.puppetlabs.http.client.impl;\n+\n+import com.codahale.metrics.Metric;\n+import com.codahale.metrics.MetricRegistry;\n+import com.codahale.metrics.Timer;\n+import org.apache.http.HttpRequest;\n+import org.apache.http.RequestLine;\n+import org.slf4j.Logger;\n+import org.slf4j.LoggerFactory;\n+\n+import java.net.URI;\n+import java.net.URISyntaxException;\n+import java.util.ArrayList;\n+import java.util.Arrays;\n+import java.util.HashMap;\n+import java.util.Map;\n+import java.util.concurrent.TimeUnit;\n+\n+public class Metrics {\n+    public static final String METRIC_NAMESPACE = \"puppetlabs.http-client.experimental\";\n+    public static final String URL_NAMESPACE = \"with-url\";\n+    public static final String URL_METHOD_NAMESPACE = \"with-url-and-method\";\n+    public static final String ID_NAMESPACE = \"with-metric-id\";\n+    public static final String BYTES_READ_STRING = \"bytes-read\";\n+    public enum MetricType { BYTES_READ; }\n+\n+    public static String metricTypeString(Metrics.MetricType metricType) {\n+        // currently this is the only metric type we have; in the future when\n+        // there are multiple types this will do something more useful\n+        return BYTES_READ_STRING;\n+    }\n+\n+    private static final Logger LOGGER = LoggerFactory.getLogger(Metrics.class);\n+\n+    synchronized private static ClientTimer getOrAddTimer(MetricRegistry metricRegistry,\n+                                                          String name, ClientTimer newTimer) {\n+        Metric timer = metricRegistry.getMetrics().get(name);\n+\n+        if ( timer == null ) {\n+            return metricRegistry.register(name, newTimer);\n+        } else if ( ClientTimer.class.isInstance(timer) ) {\n+            return (ClientTimer) timer;\n+        } else {\n+            throw new IllegalArgumentException(name +\n+                    \" is already used for a different type of metric\");\n+        }\n+    }\n+\n+    private static ArrayList<Timer.Context> startBytesReadMetricIdTimers(MetricRegistry registry,\n+                                                                         String[] metricId) {\n+        ArrayList<Timer.Context> timerContexts = new ArrayList<>();\n+        for (int i = 0; i < metricId.length; i++) {\n+            ArrayList<String> currentId = new ArrayList<>();\n+            for (int j = 0; j <= i; j++) {\n+                currentId.add(metricId[j]);\n+            }\n+            ArrayList<String> currentIdWithNamespace = new ArrayList<>();\n+            currentIdWithNamespace.add(ID_NAMESPACE);\n+            currentIdWithNamespace.addAll(currentId);\n+            currentIdWithNamespace.add(BYTES_READ_STRING);\n+            String metric_name = MetricRegistry.name(METRIC_NAMESPACE,\n+                    currentIdWithNamespace.toArray(new String[currentIdWithNamespace.size()]));\n+\n+            ClientTimer timer = new ClientTimer(metric_name, currentId, MetricType.BYTES_READ);\n+            timerContexts.add(getOrAddTimer(registry, metric_name, timer).time());\n+        }\n+        return timerContexts;\n+    }\n+\n+    private static ArrayList<Timer.Context> startBytesReadUrlTimers(MetricRegistry registry,\n+                                                                    HttpRequest request) {\n+        ArrayList<Timer.Context> timerContexts = new ArrayList<>();\n+        try {\n+            final RequestLine requestLine = request.getRequestLine();\n+            final URI uri = new URI(requestLine.getUri());\n+\n+            // if the port is not specified, `getPort()` returns -1\n+            final String port = uri.getPort() == -1 ? \"\" : \":\" + uri.getPort();\n+            final String strippedUrl = uri.getScheme() + \"://\" + uri.getHost()","path":"src/java/com/puppetlabs/http/client/impl/Metrics.java","position":79,"original_position":79,"commit_id":"b744e997496cca418b599519302891a84c8615a2","original_commit_id":"b744e997496cca418b599519302891a84c8615a2","user":{"login":"camlow325","id":5652737,"avatar_url":"https://avatars.githubusercontent.com/u/5652737?v=3","gravatar_id":"","url":"https://api.github.com/users/camlow325","html_url":"https://github.com/camlow325","followers_url":"https://api.github.com/users/camlow325/followers","following_url":"https://api.github.com/users/camlow325/following{/other_user}","gists_url":"https://api.github.com/users/camlow325/gists{/gist_id}","starred_url":"https://api.github.com/users/camlow325/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/camlow325/subscriptions","organizations_url":"https://api.github.com/users/camlow325/orgs","repos_url":"https://api.github.com/users/camlow325/repos","events_url":"https://api.github.com/users/camlow325/events{/privacy}","received_events_url":"https://api.github.com/users/camlow325/received_events","type":"User","site_admin":false},"body":"@rlinehan Yeah, that's fair.  The first example with `.getPath` is what I had in mind but you're right that you'd lose fidelity in cases where the caller had percent encoded characters that Java would not re-encode during the roundtrip.  I'm somewhat tempted to just live with that over having to manually concatenate strings to reconstruct the URL.  But I don't have any cases in mind where the concatenation would fail either, so I think using what you already have might be fine.  Curious what @cprice404 thinks on this.","created_at":"2016-04-21T22:12:33Z","updated_at":"2016-04-21T22:12:33Z","html_url":"https://github.com/puppetlabs/clj-http-client/pull/52#discussion_r60663759","pull_request_url":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/52","_links":{"self":{"href":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/comments/60663759"},"html":{"href":"https://github.com/puppetlabs/clj-http-client/pull/52#discussion_r60663759"},"pull_request":{"href":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/52"}}},"pull_request":{"url":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/52","id":67086714,"html_url":"https://github.com/puppetlabs/clj-http-client/pull/52","diff_url":"https://github.com/puppetlabs/clj-http-client/pull/52.diff","patch_url":"https://github.com/puppetlabs/clj-http-client/pull/52.patch","issue_url":"https://api.github.com/repos/puppetlabs/clj-http-client/issues/52","number":52,"state":"open","locked":false,"title":"(TK-316) Improved client metrics support","user":{"login":"rlinehan","id":1530016,"avatar_url":"https://avatars.githubusercontent.com/u/1530016?v=3","gravatar_id":"","url":"https://api.github.com/users/rlinehan","html_url":"https://github.com/rlinehan","followers_url":"https://api.github.com/users/rlinehan/followers","following_url":"https://api.github.com/users/rlinehan/following{/other_user}","gists_url":"https://api.github.com/users/rlinehan/gists{/gist_id}","starred_url":"https://api.github.com/users/rlinehan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rlinehan/subscriptions","organizations_url":"https://api.github.com/users/rlinehan/orgs","repos_url":"https://api.github.com/users/rlinehan/repos","events_url":"https://api.github.com/users/rlinehan/events{/privacy}","received_events_url":"https://api.github.com/users/rlinehan/received_events","type":"User","site_admin":false},"body":"This PR improves on #51 and hopefully addresses all feedback. In particular, it improves the metrics API by:\r\n\r\n* Moving the `get-client-metrics(-data)` functions off of the client\r\n* Adding a `with-url-and-method` namespace\r\n* Adding a `ClientTimer` class that wraps `Timer` and holds onto the metric name, and url, method, or metric id used to create the timer\r\n* Changing the `getClientMetrics(Data)` methods to return a map of metric category to an array of timers or timer data\r\n* Adding `getClientMetricsBy*` methods to filter by a url, url and method, or metric id","created_at":"2016-04-19T20:45:40Z","updated_at":"2016-04-21T22:12:33Z","closed_at":null,"merged_at":null,"merge_commit_sha":"bca907bb63d8a8f5013642f126c1b84ebc6b7dae","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/52/commits","review_comments_url":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/52/comments","review_comment_url":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/comments{/number}","comments_url":"https://api.github.com/repos/puppetlabs/clj-http-client/issues/52/comments","statuses_url":"https://api.github.com/repos/puppetlabs/clj-http-client/statuses/b744e997496cca418b599519302891a84c8615a2","head":{"label":"rlinehan:TK-316-client-metrics-again","ref":"TK-316-client-metrics-again","sha":"b744e997496cca418b599519302891a84c8615a2","user":{"login":"rlinehan","id":1530016,"avatar_url":"https://avatars.githubusercontent.com/u/1530016?v=3","gravatar_id":"","url":"https://api.github.com/users/rlinehan","html_url":"https://github.com/rlinehan","followers_url":"https://api.github.com/users/rlinehan/followers","following_url":"https://api.github.com/users/rlinehan/following{/other_user}","gists_url":"https://api.github.com/users/rlinehan/gists{/gist_id}","starred_url":"https://api.github.com/users/rlinehan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rlinehan/subscriptions","organizations_url":"https://api.github.com/users/rlinehan/orgs","repos_url":"https://api.github.com/users/rlinehan/repos","events_url":"https://api.github.com/users/rlinehan/events{/privacy}","received_events_url":"https://api.github.com/users/rlinehan/received_events","type":"User","site_admin":false},"repo":{"id":22358809,"name":"clj-http-client","full_name":"rlinehan/clj-http-client","owner":{"login":"rlinehan","id":1530016,"avatar_url":"https://avatars.githubusercontent.com/u/1530016?v=3","gravatar_id":"","url":"https://api.github.com/users/rlinehan","html_url":"https://github.com/rlinehan","followers_url":"https://api.github.com/users/rlinehan/followers","following_url":"https://api.github.com/users/rlinehan/following{/other_user}","gists_url":"https://api.github.com/users/rlinehan/gists{/gist_id}","starred_url":"https://api.github.com/users/rlinehan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rlinehan/subscriptions","organizations_url":"https://api.github.com/users/rlinehan/orgs","repos_url":"https://api.github.com/users/rlinehan/repos","events_url":"https://api.github.com/users/rlinehan/events{/privacy}","received_events_url":"https://api.github.com/users/rlinehan/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/rlinehan/clj-http-client","description":"HTTP client library wrapping http-kit","fork":true,"url":"https://api.github.com/repos/rlinehan/clj-http-client","forks_url":"https://api.github.com/repos/rlinehan/clj-http-client/forks","keys_url":"https://api.github.com/repos/rlinehan/clj-http-client/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rlinehan/clj-http-client/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rlinehan/clj-http-client/teams","hooks_url":"https://api.github.com/repos/rlinehan/clj-http-client/hooks","issue_events_url":"https://api.github.com/repos/rlinehan/clj-http-client/issues/events{/number}","events_url":"https://api.github.com/repos/rlinehan/clj-http-client/events","assignees_url":"https://api.github.com/repos/rlinehan/clj-http-client/assignees{/user}","branches_url":"https://api.github.com/repos/rlinehan/clj-http-client/branches{/branch}","tags_url":"https://api.github.com/repos/rlinehan/clj-http-client/tags","blobs_url":"https://api.github.com/repos/rlinehan/clj-http-client/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rlinehan/clj-http-client/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rlinehan/clj-http-client/git/refs{/sha}","trees_url":"https://api.github.com/repos/rlinehan/clj-http-client/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rlinehan/clj-http-client/statuses/{sha}","languages_url":"https://api.github.com/repos/rlinehan/clj-http-client/languages","stargazers_url":"https://api.github.com/repos/rlinehan/clj-http-client/stargazers","contributors_url":"https://api.github.com/repos/rlinehan/clj-http-client/contributors","subscribers_url":"https://api.github.com/repos/rlinehan/clj-http-client/subscribers","subscription_url":"https://api.github.com/repos/rlinehan/clj-http-client/subscription","commits_url":"https://api.github.com/repos/rlinehan/clj-http-client/commits{/sha}","git_commits_url":"https://api.github.com/repos/rlinehan/clj-http-client/git/commits{/sha}","comments_url":"https://api.github.com/repos/rlinehan/clj-http-client/comments{/number}","issue_comment_url":"https://api.github.com/repos/rlinehan/clj-http-client/issues/comments{/number}","contents_url":"https://api.github.com/repos/rlinehan/clj-http-client/contents/{+path}","compare_url":"https://api.github.com/repos/rlinehan/clj-http-client/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rlinehan/clj-http-client/merges","archive_url":"https://api.github.com/repos/rlinehan/clj-http-client/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rlinehan/clj-http-client/downloads","issues_url":"https://api.github.com/repos/rlinehan/clj-http-client/issues{/number}","pulls_url":"https://api.github.com/repos/rlinehan/clj-http-client/pulls{/number}","milestones_url":"https://api.github.com/repos/rlinehan/clj-http-client/milestones{/number}","notifications_url":"https://api.github.com/repos/rlinehan/clj-http-client/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rlinehan/clj-http-client/labels{/name}","releases_url":"https://api.github.com/repos/rlinehan/clj-http-client/releases{/id}","deployments_url":"https://api.github.com/repos/rlinehan/clj-http-client/deployments","created_at":"2014-07-28T21:38:56Z","updated_at":"2016-01-21T11:06:59Z","pushed_at":"2016-04-19T20:13:26Z","git_url":"git://github.com/rlinehan/clj-http-client.git","ssh_url":"git@github.com:rlinehan/clj-http-client.git","clone_url":"https://github.com/rlinehan/clj-http-client.git","svn_url":"https://github.com/rlinehan/clj-http-client","homepage":null,"size":579,"stargazers_count":0,"watchers_count":0,"language":"Clojure","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":0,"default_branch":"master"}},"base":{"label":"puppetlabs:master","ref":"master","sha":"fb39b412605b451215a56d6be579af2d33dd0ab3","user":{"login":"puppetlabs","id":234268,"avatar_url":"https://avatars.githubusercontent.com/u/234268?v=3","gravatar_id":"","url":"https://api.github.com/users/puppetlabs","html_url":"https://github.com/puppetlabs","followers_url":"https://api.github.com/users/puppetlabs/followers","following_url":"https://api.github.com/users/puppetlabs/following{/other_user}","gists_url":"https://api.github.com/users/puppetlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/puppetlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/puppetlabs/subscriptions","organizations_url":"https://api.github.com/users/puppetlabs/orgs","repos_url":"https://api.github.com/users/puppetlabs/repos","events_url":"https://api.github.com/users/puppetlabs/events{/privacy}","received_events_url":"https://api.github.com/users/puppetlabs/received_events","type":"Organization","site_admin":false},"repo":{"id":17650370,"name":"clj-http-client","full_name":"puppetlabs/clj-http-client","owner":{"login":"puppetlabs","id":234268,"avatar_url":"https://avatars.githubusercontent.com/u/234268?v=3","gravatar_id":"","url":"https://api.github.com/users/puppetlabs","html_url":"https://github.com/puppetlabs","followers_url":"https://api.github.com/users/puppetlabs/followers","following_url":"https://api.github.com/users/puppetlabs/following{/other_user}","gists_url":"https://api.github.com/users/puppetlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/puppetlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/puppetlabs/subscriptions","organizations_url":"https://api.github.com/users/puppetlabs/orgs","repos_url":"https://api.github.com/users/puppetlabs/repos","events_url":"https://api.github.com/users/puppetlabs/events{/privacy}","received_events_url":"https://api.github.com/users/puppetlabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/puppetlabs/clj-http-client","description":"HTTP client library wrapping Apache HttpAsyncClient","fork":false,"url":"https://api.github.com/repos/puppetlabs/clj-http-client","forks_url":"https://api.github.com/repos/puppetlabs/clj-http-client/forks","keys_url":"https://api.github.com/repos/puppetlabs/clj-http-client/keys{/key_id}","collaborators_url":"https://api.github.com/repos/puppetlabs/clj-http-client/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/puppetlabs/clj-http-client/teams","hooks_url":"https://api.github.com/repos/puppetlabs/clj-http-client/hooks","issue_events_url":"https://api.github.com/repos/puppetlabs/clj-http-client/issues/events{/number}","events_url":"https://api.github.com/repos/puppetlabs/clj-http-client/events","assignees_url":"https://api.github.com/repos/puppetlabs/clj-http-client/assignees{/user}","branches_url":"https://api.github.com/repos/puppetlabs/clj-http-client/branches{/branch}","tags_url":"https://api.github.com/repos/puppetlabs/clj-http-client/tags","blobs_url":"https://api.github.com/repos/puppetlabs/clj-http-client/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/puppetlabs/clj-http-client/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/puppetlabs/clj-http-client/git/refs{/sha}","trees_url":"https://api.github.com/repos/puppetlabs/clj-http-client/git/trees{/sha}","statuses_url":"https://api.github.com/repos/puppetlabs/clj-http-client/statuses/{sha}","languages_url":"https://api.github.com/repos/puppetlabs/clj-http-client/languages","stargazers_url":"https://api.github.com/repos/puppetlabs/clj-http-client/stargazers","contributors_url":"https://api.github.com/repos/puppetlabs/clj-http-client/contributors","subscribers_url":"https://api.github.com/repos/puppetlabs/clj-http-client/subscribers","subscription_url":"https://api.github.com/repos/puppetlabs/clj-http-client/subscription","commits_url":"https://api.github.com/repos/puppetlabs/clj-http-client/commits{/sha}","git_commits_url":"https://api.github.com/repos/puppetlabs/clj-http-client/git/commits{/sha}","comments_url":"https://api.github.com/repos/puppetlabs/clj-http-client/comments{/number}","issue_comment_url":"https://api.github.com/repos/puppetlabs/clj-http-client/issues/comments{/number}","contents_url":"https://api.github.com/repos/puppetlabs/clj-http-client/contents/{+path}","compare_url":"https://api.github.com/repos/puppetlabs/clj-http-client/compare/{base}...{head}","merges_url":"https://api.github.com/repos/puppetlabs/clj-http-client/merges","archive_url":"https://api.github.com/repos/puppetlabs/clj-http-client/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/puppetlabs/clj-http-client/downloads","issues_url":"https://api.github.com/repos/puppetlabs/clj-http-client/issues{/number}","pulls_url":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls{/number}","milestones_url":"https://api.github.com/repos/puppetlabs/clj-http-client/milestones{/number}","notifications_url":"https://api.github.com/repos/puppetlabs/clj-http-client/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/puppetlabs/clj-http-client/labels{/name}","releases_url":"https://api.github.com/repos/puppetlabs/clj-http-client/releases{/id}","deployments_url":"https://api.github.com/repos/puppetlabs/clj-http-client/deployments","created_at":"2014-03-12T00:39:04Z","updated_at":"2016-01-19T17:20:53Z","pushed_at":"2016-04-19T20:45:40Z","git_url":"git://github.com/puppetlabs/clj-http-client.git","ssh_url":"git@github.com:puppetlabs/clj-http-client.git","clone_url":"https://github.com/puppetlabs/clj-http-client.git","svn_url":"https://github.com/puppetlabs/clj-http-client","homepage":"","size":433,"stargazers_count":2,"watchers_count":2,"language":"Clojure","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":15,"mirror_url":null,"open_issues_count":1,"forks":15,"open_issues":1,"watchers":2,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/52"},"html":{"href":"https://github.com/puppetlabs/clj-http-client/pull/52"},"issue":{"href":"https://api.github.com/repos/puppetlabs/clj-http-client/issues/52"},"comments":{"href":"https://api.github.com/repos/puppetlabs/clj-http-client/issues/52/comments"},"review_comments":{"href":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/52/comments"},"review_comment":{"href":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/puppetlabs/clj-http-client/pulls/52/commits"},"statuses":{"href":"https://api.github.com/repos/puppetlabs/clj-http-client/statuses/b744e997496cca418b599519302891a84c8615a2"}}}}