{"action":"created","comment":{"url":"https://api.github.com/repos/prometheus/prometheus/pulls/comments/44213518","id":44213518,"diff_hunk":"@@ -0,0 +1,90 @@\n+package graphite\n+\n+import (\n+\t\"bytes\"\n+\t\"fmt\"\n+\t\"strings\"\n+\n+\t\"github.com/prometheus/common/model\"\n+)\n+\n+const (\n+\t// From https://github.com/graphite-project/graphite-web/blob/master/webapp/graphite/render/grammar.py#L83\n+\tsymbols    = \"(){},=.'\\\"\\\\\"\n+\tprintables = (\"0123456789abcdefghijklmnopqrstuvwxyz\" +\n+\t\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" +\n+\t\t\"!\\\"#$%&\\\\'()*+,-./:;<=>?@[\\\\]^_`{|}~\")\n+)\n+\n+// Graphite doesn't support tags, so label names and values must be\n+// encoded into the metric path. The list of characters that are usable\n+// with Graphite is rather fuzzy. One 'source of truth' might be the grammar\n+// used to parse requests in the webapp:\n+// https://github.com/graphite-project/graphite-web/blob/master/webapp/graphite/render/grammar.py#L83\n+// The list of valid symbols is defined as:\n+//   legal = printables - symbols + escaped(symbols)\n+//\n+// The default storage backend for Graphite (whisper) stores data\n+// in filenames, so we also need to use only valid filename characters.\n+// Fortunately on UNIX only '/' isn't, and Windows is completely unsupported\n+// by Graphite: http://graphite.readthedocs.org/en/latest/install.html#windows-users\n+\n+// escape escapes a model.LabelValue into runes allowed in Graphite. The runes\n+// allowed in Graphite are all single-byte. This function encodes the arbitrary\n+// byte sequence found in this TagValue in way very similar to the traditional\n+// percent-encoding (https://en.wikipedia.org/wiki/Percent-encoding):\n+//\n+// - The string that underlies TagValue is scanned byte by byte.\n+//\n+// - If a byte represents a legal Graphite rune with the exception of '%', '/',\n+//   '=' and '.', that byte is directly copied to the resulting byte slice.\n+//   % is used for percent-encoding of other bytes.\n+//   / is not usable in filenames.\n+//   = is used when generating the path to associate values to labels.\n+//   . already means something for Graphite and thus can't be used in a value.\n+//\n+// - If the byte is any of (){},=.'\"\\, then a '\\' will be prepended to it. We\n+//   do not percent-encode them since they are explicitely usable in this\n+//   way in Graphite.\n+//\n+// - All other bytes are replaced by '%' followed by two bytes containing the\n+//   uppercase ASCII representation of their hexadecimal value.\n+//\n+// This encoding allows to save arbitrary Go strings in Graphite. That's\n+// required because Prometheus label values can contain anything. Using\n+// percent encoding makes it easy to unescape, even in javascript.\n+//\n+// Examples:\n+//\n+// \"foo-bar-42\" -> \"foo-bar-42\"\n+//\n+// \"foo_bar%42\" -> \"foo_bar%2542\"\n+//\n+// \"http://example.org:8080\" -> \"http:%2F%2Fexample%2Eorg:8080\"\n+//\n+// \"Björn's email: bjoern@soundcloud.com\" ->\n+// \"Bj%C3%B6rn's%20email:%20bjoern%40soundcloud.com\"\n+//\n+// \"日\" -> \"%E6%97%A5\"\n+func escape(tv model.LabelValue) string {\n+\tlength := len(tv)\n+\tresult := bytes.NewBufferString(\"\")\n+\tfor i := 0; i < length; i++ {\n+\t\tb := tv[i]\n+\t\tswitch {\n+\t\t// . is reserved by graphite, % is used to escape other bytes.\n+\t\tcase (b == '.' || b == '%' || b == '/' || b == '='):\n+\t\t\tresult.WriteString(fmt.Sprintf(\"%%%X\", b))\n+\t\t// These symbols are ok only if backslash escaped.\n+\t\tcase (strings.IndexByte(symbols, b) != -1):\n+\t\t\tresult.WriteString(\"\\\\\" + string(b))\n+\t\t// These are all fine.\n+\t\tcase (strings.IndexByte(printables, b) != -1):\n+\t\t\tresult.WriteByte(b)\n+\t\t// Defaults to percent-encoding.\n+\t\tdefault:\n+\t\t\tresult.WriteString(fmt.Sprintf(\"%%%X\", b))\n+\t\t}\n+\t}\n+\treturn string(result.Bytes())","path":"storage/remote/graphite/escape.go","position":89,"original_position":89,"commit_id":"fce5eec5b65f6931df4ceadd30ea889acd373013","original_commit_id":"fce5eec5b65f6931df4ceadd30ea889acd373013","user":{"login":"juliusv","id":538008,"avatar_url":"https://avatars.githubusercontent.com/u/538008?v=3","gravatar_id":"","url":"https://api.github.com/users/juliusv","html_url":"https://github.com/juliusv","followers_url":"https://api.github.com/users/juliusv/followers","following_url":"https://api.github.com/users/juliusv/following{/other_user}","gists_url":"https://api.github.com/users/juliusv/gists{/gist_id}","starred_url":"https://api.github.com/users/juliusv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/juliusv/subscriptions","organizations_url":"https://api.github.com/users/juliusv/orgs","repos_url":"https://api.github.com/users/juliusv/repos","events_url":"https://api.github.com/users/juliusv/events{/privacy}","received_events_url":"https://api.github.com/users/juliusv/received_events","type":"User","site_admin":false},"body":"This can be just `return result.String()`.","created_at":"2015-11-07T15:44:21Z","updated_at":"2015-11-07T15:44:21Z","html_url":"https://github.com/prometheus/prometheus/pull/1197#discussion_r44213518","pull_request_url":"https://api.github.com/repos/prometheus/prometheus/pulls/1197","_links":{"self":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/comments/44213518"},"html":{"href":"https://github.com/prometheus/prometheus/pull/1197#discussion_r44213518"},"pull_request":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/1197"}}},"pull_request":{"url":"https://api.github.com/repos/prometheus/prometheus/pulls/1197","id":49402534,"html_url":"https://github.com/prometheus/prometheus/pull/1197","diff_url":"https://github.com/prometheus/prometheus/pull/1197.diff","patch_url":"https://github.com/prometheus/prometheus/pull/1197.patch","issue_url":"https://api.github.com/repos/prometheus/prometheus/issues/1197","number":1197,"state":"open","locked":false,"title":"Add support for remote storage on Graphite","user":{"login":"iksaif","id":1032963,"avatar_url":"https://avatars.githubusercontent.com/u/1032963?v=3","gravatar_id":"","url":"https://api.github.com/users/iksaif","html_url":"https://github.com/iksaif","followers_url":"https://api.github.com/users/iksaif/followers","following_url":"https://api.github.com/users/iksaif/following{/other_user}","gists_url":"https://api.github.com/users/iksaif/gists{/gist_id}","starred_url":"https://api.github.com/users/iksaif/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iksaif/subscriptions","organizations_url":"https://api.github.com/users/iksaif/orgs","repos_url":"https://api.github.com/users/iksaif/repos","events_url":"https://api.github.com/users/iksaif/events{/privacy}","received_events_url":"https://api.github.com/users/iksaif/received_events","type":"User","site_admin":false},"body":"Allows to use graphite over tcp or udp. Metrics labels\r\nand values are used to construct a valid Graphite path\r\nin a way that will allow us to eventually read them back\r\nand reconstruct the metrics.\r\n\r\nFor example, this metric:\r\n\r\nmodel.Metric{\r\n\tmodel.MetricNameLabel: \"test:metric\",\r\n\t\"testlabel\":           \"test:value\",\r\n\t\"testlabel2\":           \"test:value\",\r\n)\r\n\r\nWill become:\r\n\r\ntest:metric.testlabel=test:value.testlabel2=test:value\r\n\r\nescape.go takes care of escaping values to match Graphite\r\ncharacter set, it basically uses percent-encoding as a fallback\r\nwich will work pretty will in the graphite/grafana world.\r\n\r\nThe remote storage module also has an optional 'prefix' parameter\r\nto prefix all metrics with a path (for example, 'prometheus.').\r\n\r\nGraphite URLs are simply in the form tcp://host:port or\r\nudp://host:port.\r\n\r\n![prometheus-graphite](https://cloud.githubusercontent.com/assets/1032963/10902928/423bd62a-8204-11e5-8b98-95251132671f.png)","created_at":"2015-11-02T07:41:07Z","updated_at":"2015-11-07T15:44:21Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e86d89bf16ca13ed8d0518ed73a97b91add390d6","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/prometheus/prometheus/pulls/1197/commits","review_comments_url":"https://api.github.com/repos/prometheus/prometheus/pulls/1197/comments","review_comment_url":"https://api.github.com/repos/prometheus/prometheus/pulls/comments{/number}","comments_url":"https://api.github.com/repos/prometheus/prometheus/issues/1197/comments","statuses_url":"https://api.github.com/repos/prometheus/prometheus/statuses/fce5eec5b65f6931df4ceadd30ea889acd373013","head":{"label":"iksaif:master","ref":"master","sha":"fce5eec5b65f6931df4ceadd30ea889acd373013","user":{"login":"iksaif","id":1032963,"avatar_url":"https://avatars.githubusercontent.com/u/1032963?v=3","gravatar_id":"","url":"https://api.github.com/users/iksaif","html_url":"https://github.com/iksaif","followers_url":"https://api.github.com/users/iksaif/followers","following_url":"https://api.github.com/users/iksaif/following{/other_user}","gists_url":"https://api.github.com/users/iksaif/gists{/gist_id}","starred_url":"https://api.github.com/users/iksaif/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iksaif/subscriptions","organizations_url":"https://api.github.com/users/iksaif/orgs","repos_url":"https://api.github.com/users/iksaif/repos","events_url":"https://api.github.com/users/iksaif/events{/privacy}","received_events_url":"https://api.github.com/users/iksaif/received_events","type":"User","site_admin":false},"repo":{"id":44734864,"name":"prometheus","full_name":"iksaif/prometheus","owner":{"login":"iksaif","id":1032963,"avatar_url":"https://avatars.githubusercontent.com/u/1032963?v=3","gravatar_id":"","url":"https://api.github.com/users/iksaif","html_url":"https://github.com/iksaif","followers_url":"https://api.github.com/users/iksaif/followers","following_url":"https://api.github.com/users/iksaif/following{/other_user}","gists_url":"https://api.github.com/users/iksaif/gists{/gist_id}","starred_url":"https://api.github.com/users/iksaif/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iksaif/subscriptions","organizations_url":"https://api.github.com/users/iksaif/orgs","repos_url":"https://api.github.com/users/iksaif/repos","events_url":"https://api.github.com/users/iksaif/events{/privacy}","received_events_url":"https://api.github.com/users/iksaif/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/iksaif/prometheus","description":"The Prometheus monitoring system and time series database.","fork":true,"url":"https://api.github.com/repos/iksaif/prometheus","forks_url":"https://api.github.com/repos/iksaif/prometheus/forks","keys_url":"https://api.github.com/repos/iksaif/prometheus/keys{/key_id}","collaborators_url":"https://api.github.com/repos/iksaif/prometheus/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/iksaif/prometheus/teams","hooks_url":"https://api.github.com/repos/iksaif/prometheus/hooks","issue_events_url":"https://api.github.com/repos/iksaif/prometheus/issues/events{/number}","events_url":"https://api.github.com/repos/iksaif/prometheus/events","assignees_url":"https://api.github.com/repos/iksaif/prometheus/assignees{/user}","branches_url":"https://api.github.com/repos/iksaif/prometheus/branches{/branch}","tags_url":"https://api.github.com/repos/iksaif/prometheus/tags","blobs_url":"https://api.github.com/repos/iksaif/prometheus/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/iksaif/prometheus/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/iksaif/prometheus/git/refs{/sha}","trees_url":"https://api.github.com/repos/iksaif/prometheus/git/trees{/sha}","statuses_url":"https://api.github.com/repos/iksaif/prometheus/statuses/{sha}","languages_url":"https://api.github.com/repos/iksaif/prometheus/languages","stargazers_url":"https://api.github.com/repos/iksaif/prometheus/stargazers","contributors_url":"https://api.github.com/repos/iksaif/prometheus/contributors","subscribers_url":"https://api.github.com/repos/iksaif/prometheus/subscribers","subscription_url":"https://api.github.com/repos/iksaif/prometheus/subscription","commits_url":"https://api.github.com/repos/iksaif/prometheus/commits{/sha}","git_commits_url":"https://api.github.com/repos/iksaif/prometheus/git/commits{/sha}","comments_url":"https://api.github.com/repos/iksaif/prometheus/comments{/number}","issue_comment_url":"https://api.github.com/repos/iksaif/prometheus/issues/comments{/number}","contents_url":"https://api.github.com/repos/iksaif/prometheus/contents/{+path}","compare_url":"https://api.github.com/repos/iksaif/prometheus/compare/{base}...{head}","merges_url":"https://api.github.com/repos/iksaif/prometheus/merges","archive_url":"https://api.github.com/repos/iksaif/prometheus/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/iksaif/prometheus/downloads","issues_url":"https://api.github.com/repos/iksaif/prometheus/issues{/number}","pulls_url":"https://api.github.com/repos/iksaif/prometheus/pulls{/number}","milestones_url":"https://api.github.com/repos/iksaif/prometheus/milestones{/number}","notifications_url":"https://api.github.com/repos/iksaif/prometheus/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/iksaif/prometheus/labels{/name}","releases_url":"https://api.github.com/repos/iksaif/prometheus/releases{/id}","created_at":"2015-10-22T09:18:14Z","updated_at":"2015-10-22T09:18:16Z","pushed_at":"2015-11-03T07:22:29Z","git_url":"git://github.com/iksaif/prometheus.git","ssh_url":"git@github.com:iksaif/prometheus.git","clone_url":"https://github.com/iksaif/prometheus.git","svn_url":"https://github.com/iksaif/prometheus","homepage":"http://prometheus.io/","size":9025,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"prometheus:master","ref":"master","sha":"a2072d617da7cb9886678a709aa0812e38cac5f7","user":{"login":"prometheus","id":3380462,"avatar_url":"https://avatars.githubusercontent.com/u/3380462?v=3","gravatar_id":"","url":"https://api.github.com/users/prometheus","html_url":"https://github.com/prometheus","followers_url":"https://api.github.com/users/prometheus/followers","following_url":"https://api.github.com/users/prometheus/following{/other_user}","gists_url":"https://api.github.com/users/prometheus/gists{/gist_id}","starred_url":"https://api.github.com/users/prometheus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prometheus/subscriptions","organizations_url":"https://api.github.com/users/prometheus/orgs","repos_url":"https://api.github.com/users/prometheus/repos","events_url":"https://api.github.com/users/prometheus/events{/privacy}","received_events_url":"https://api.github.com/users/prometheus/received_events","type":"Organization","site_admin":false},"repo":{"id":6838921,"name":"prometheus","full_name":"prometheus/prometheus","owner":{"login":"prometheus","id":3380462,"avatar_url":"https://avatars.githubusercontent.com/u/3380462?v=3","gravatar_id":"","url":"https://api.github.com/users/prometheus","html_url":"https://github.com/prometheus","followers_url":"https://api.github.com/users/prometheus/followers","following_url":"https://api.github.com/users/prometheus/following{/other_user}","gists_url":"https://api.github.com/users/prometheus/gists{/gist_id}","starred_url":"https://api.github.com/users/prometheus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prometheus/subscriptions","organizations_url":"https://api.github.com/users/prometheus/orgs","repos_url":"https://api.github.com/users/prometheus/repos","events_url":"https://api.github.com/users/prometheus/events{/privacy}","received_events_url":"https://api.github.com/users/prometheus/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/prometheus/prometheus","description":"The Prometheus monitoring system and time series database.","fork":false,"url":"https://api.github.com/repos/prometheus/prometheus","forks_url":"https://api.github.com/repos/prometheus/prometheus/forks","keys_url":"https://api.github.com/repos/prometheus/prometheus/keys{/key_id}","collaborators_url":"https://api.github.com/repos/prometheus/prometheus/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/prometheus/prometheus/teams","hooks_url":"https://api.github.com/repos/prometheus/prometheus/hooks","issue_events_url":"https://api.github.com/repos/prometheus/prometheus/issues/events{/number}","events_url":"https://api.github.com/repos/prometheus/prometheus/events","assignees_url":"https://api.github.com/repos/prometheus/prometheus/assignees{/user}","branches_url":"https://api.github.com/repos/prometheus/prometheus/branches{/branch}","tags_url":"https://api.github.com/repos/prometheus/prometheus/tags","blobs_url":"https://api.github.com/repos/prometheus/prometheus/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/prometheus/prometheus/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/prometheus/prometheus/git/refs{/sha}","trees_url":"https://api.github.com/repos/prometheus/prometheus/git/trees{/sha}","statuses_url":"https://api.github.com/repos/prometheus/prometheus/statuses/{sha}","languages_url":"https://api.github.com/repos/prometheus/prometheus/languages","stargazers_url":"https://api.github.com/repos/prometheus/prometheus/stargazers","contributors_url":"https://api.github.com/repos/prometheus/prometheus/contributors","subscribers_url":"https://api.github.com/repos/prometheus/prometheus/subscribers","subscription_url":"https://api.github.com/repos/prometheus/prometheus/subscription","commits_url":"https://api.github.com/repos/prometheus/prometheus/commits{/sha}","git_commits_url":"https://api.github.com/repos/prometheus/prometheus/git/commits{/sha}","comments_url":"https://api.github.com/repos/prometheus/prometheus/comments{/number}","issue_comment_url":"https://api.github.com/repos/prometheus/prometheus/issues/comments{/number}","contents_url":"https://api.github.com/repos/prometheus/prometheus/contents/{+path}","compare_url":"https://api.github.com/repos/prometheus/prometheus/compare/{base}...{head}","merges_url":"https://api.github.com/repos/prometheus/prometheus/merges","archive_url":"https://api.github.com/repos/prometheus/prometheus/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/prometheus/prometheus/downloads","issues_url":"https://api.github.com/repos/prometheus/prometheus/issues{/number}","pulls_url":"https://api.github.com/repos/prometheus/prometheus/pulls{/number}","milestones_url":"https://api.github.com/repos/prometheus/prometheus/milestones{/number}","notifications_url":"https://api.github.com/repos/prometheus/prometheus/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/prometheus/prometheus/labels{/name}","releases_url":"https://api.github.com/repos/prometheus/prometheus/releases{/id}","created_at":"2012-11-24T11:14:12Z","updated_at":"2015-11-07T11:26:22Z","pushed_at":"2015-11-07T14:40:46Z","git_url":"git://github.com/prometheus/prometheus.git","ssh_url":"git@github.com:prometheus/prometheus.git","clone_url":"https://github.com/prometheus/prometheus.git","svn_url":"https://github.com/prometheus/prometheus","homepage":"http://prometheus.io/","size":19915,"stargazers_count":3298,"watchers_count":3298,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":238,"mirror_url":null,"open_issues_count":106,"forks":238,"open_issues":106,"watchers":3298,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/1197"},"html":{"href":"https://github.com/prometheus/prometheus/pull/1197"},"issue":{"href":"https://api.github.com/repos/prometheus/prometheus/issues/1197"},"comments":{"href":"https://api.github.com/repos/prometheus/prometheus/issues/1197/comments"},"review_comments":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/1197/comments"},"review_comment":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/1197/commits"},"statuses":{"href":"https://api.github.com/repos/prometheus/prometheus/statuses/fce5eec5b65f6931df4ceadd30ea889acd373013"}}}}