{"action":"created","comment":{"url":"https://api.github.com/repos/mozilla-services/heka/pulls/comments/32879581","id":32879581,"diff_hunk":"@@ -371,6 +373,53 @@ func (k *KafkaOutput) CleanupForRestart() {\n \treturn\n }\n \n+// Escapes a topic name by encoding any non-valid kafka topic name\n+func topicEscape(s string) string {\n+\thexCount := 0\n+\tfor i := 0; i < len(s); i++ {\n+\t\tif shouldEscape(s[i]) {\n+\t\t\thexCount++\n+\t\t}\n+\t}\n+\n+\tif hexCount == 0 {\n+\t\treturn s\n+\t}\n+\n+\tt := make([]byte, len(s)+2*hexCount)\n+\tj := 0\n+\tfor i := 0; i < len(s); i++ {\n+\t\tc := s[i]\n+\t\tif shouldEscape(c) {\n+\t\t\tt[j] = '-'\n+\t\t\tt[j+1] = \"0123456789ABCDEF\"[c>>4]\n+\t\t\tt[j+2] = \"0123456789ABCDEF\"[c&15]\n+\t\t\tj += 3\n+\t\t} else {\n+\t\t\tt[j] = s[i]\n+\t\t\tj++\n+\t\t}\n+\t}\n+\treturn string(t)\n+}\n+\n+func shouldEscape(c byte) bool {\n+\t// List of valid kafka topic characters can be found at:\n+\t// https://github.com/apache/kafka/blob/43b92f8b1ce8140c432edf11b0c842f5fbe04120/core/src/main/scala/kafka/common/Topic.scala#L25\n+\t//   val legalChars = \"[a-zA-Z0-9\\\\._\\\\-]\"\n+\tif 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {\n+\t\treturn false\n+\t}\n+\n+\tswitch c {\n+\tcase '.', '_', '-':","path":"plugins/kafka/kafka_output.go","position":74,"original_position":74,"commit_id":"715202094891b7c26987f16048e59e79effe9ef5","original_commit_id":"715202094891b7c26987f16048e59e79effe9ef5","user":{"login":"rafrombrc","id":151364,"avatar_url":"https://avatars.githubusercontent.com/u/151364?v=3","gravatar_id":"","url":"https://api.github.com/users/rafrombrc","html_url":"https://github.com/rafrombrc","followers_url":"https://api.github.com/users/rafrombrc/followers","following_url":"https://api.github.com/users/rafrombrc/following{/other_user}","gists_url":"https://api.github.com/users/rafrombrc/gists{/gist_id}","starred_url":"https://api.github.com/users/rafrombrc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rafrombrc/subscriptions","organizations_url":"https://api.github.com/users/rafrombrc/orgs","repos_url":"https://api.github.com/users/rafrombrc/repos","events_url":"https://api.github.com/users/rafrombrc/events{/privacy}","received_events_url":"https://api.github.com/users/rafrombrc/received_events","type":"User","site_admin":false},"body":"Not sure I understand. Can't we use a different character for escaping? Back-slashes aren't allowed in a topic name, but they could still be used as an escape character, right? I might be missing something, though...","created_at":"2015-06-20T00:26:31Z","updated_at":"2015-06-20T00:26:31Z","html_url":"https://github.com/mozilla-services/heka/pull/1570#discussion_r32879581","pull_request_url":"https://api.github.com/repos/mozilla-services/heka/pulls/1570","_links":{"self":{"href":"https://api.github.com/repos/mozilla-services/heka/pulls/comments/32879581"},"html":{"href":"https://github.com/mozilla-services/heka/pull/1570#discussion_r32879581"},"pull_request":{"href":"https://api.github.com/repos/mozilla-services/heka/pulls/1570"}}},"pull_request":{"url":"https://api.github.com/repos/mozilla-services/heka/pulls/1570","id":37318436,"html_url":"https://github.com/mozilla-services/heka/pull/1570","diff_url":"https://github.com/mozilla-services/heka/pull/1570.diff","patch_url":"https://github.com/mozilla-services/heka/pull/1570.patch","issue_url":"https://api.github.com/repos/mozilla-services/heka/issues/1570","number":1570,"state":"open","locked":false,"title":"kafka: Add `topic_prefix` and escape topic names","user":{"login":"mattrobenolt","id":375744,"avatar_url":"https://avatars.githubusercontent.com/u/375744?v=3","gravatar_id":"","url":"https://api.github.com/users/mattrobenolt","html_url":"https://github.com/mattrobenolt","followers_url":"https://api.github.com/users/mattrobenolt/followers","following_url":"https://api.github.com/users/mattrobenolt/following{/other_user}","gists_url":"https://api.github.com/users/mattrobenolt/gists{/gist_id}","starred_url":"https://api.github.com/users/mattrobenolt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattrobenolt/subscriptions","organizations_url":"https://api.github.com/users/mattrobenolt/orgs","repos_url":"https://api.github.com/users/mattrobenolt/repos","events_url":"https://api.github.com/users/mattrobenolt/events{/privacy}","received_events_url":"https://api.github.com/users/mattrobenolt/received_events","type":"User","site_admin":false},"body":"These are especially useful when paired with `topic_variable`.\r\nSince the variable is being extracted from a field, we can't guarantee\r\nthat the characters being used for the topic name are valid, so\r\nwe implement a slightly modified version of stdlib's `QueryEncode`\r\nwhich sanitizes the name to the range of valid topic chars.\r\n\r\nExample:\r\n\r\n```toml\r\n[KafkaOutput]\r\ntopic_prefix = \"heka-\"\r\ntopic_variable = \"Fields[ContainerName]\"\r\n```","created_at":"2015-06-10T02:07:03Z","updated_at":"2015-06-20T00:26:31Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2fad77d55cf11b7ee2e313b8748593f76354557a","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/mozilla-services/heka/pulls/1570/commits","review_comments_url":"https://api.github.com/repos/mozilla-services/heka/pulls/1570/comments","review_comment_url":"https://api.github.com/repos/mozilla-services/heka/pulls/comments{/number}","comments_url":"https://api.github.com/repos/mozilla-services/heka/issues/1570/comments","statuses_url":"https://api.github.com/repos/mozilla-services/heka/statuses/715202094891b7c26987f16048e59e79effe9ef5","head":{"label":"mattrobenolt:kafka2","ref":"kafka2","sha":"715202094891b7c26987f16048e59e79effe9ef5","user":{"login":"mattrobenolt","id":375744,"avatar_url":"https://avatars.githubusercontent.com/u/375744?v=3","gravatar_id":"","url":"https://api.github.com/users/mattrobenolt","html_url":"https://github.com/mattrobenolt","followers_url":"https://api.github.com/users/mattrobenolt/followers","following_url":"https://api.github.com/users/mattrobenolt/following{/other_user}","gists_url":"https://api.github.com/users/mattrobenolt/gists{/gist_id}","starred_url":"https://api.github.com/users/mattrobenolt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattrobenolt/subscriptions","organizations_url":"https://api.github.com/users/mattrobenolt/orgs","repos_url":"https://api.github.com/users/mattrobenolt/repos","events_url":"https://api.github.com/users/mattrobenolt/events{/privacy}","received_events_url":"https://api.github.com/users/mattrobenolt/received_events","type":"User","site_admin":false},"repo":{"id":37112094,"name":"heka","full_name":"mattrobenolt/heka","owner":{"login":"mattrobenolt","id":375744,"avatar_url":"https://avatars.githubusercontent.com/u/375744?v=3","gravatar_id":"","url":"https://api.github.com/users/mattrobenolt","html_url":"https://github.com/mattrobenolt","followers_url":"https://api.github.com/users/mattrobenolt/followers","following_url":"https://api.github.com/users/mattrobenolt/following{/other_user}","gists_url":"https://api.github.com/users/mattrobenolt/gists{/gist_id}","starred_url":"https://api.github.com/users/mattrobenolt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattrobenolt/subscriptions","organizations_url":"https://api.github.com/users/mattrobenolt/orgs","repos_url":"https://api.github.com/users/mattrobenolt/repos","events_url":"https://api.github.com/users/mattrobenolt/events{/privacy}","received_events_url":"https://api.github.com/users/mattrobenolt/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mattrobenolt/heka","description":"Data collection and processing made easy.","fork":true,"url":"https://api.github.com/repos/mattrobenolt/heka","forks_url":"https://api.github.com/repos/mattrobenolt/heka/forks","keys_url":"https://api.github.com/repos/mattrobenolt/heka/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mattrobenolt/heka/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mattrobenolt/heka/teams","hooks_url":"https://api.github.com/repos/mattrobenolt/heka/hooks","issue_events_url":"https://api.github.com/repos/mattrobenolt/heka/issues/events{/number}","events_url":"https://api.github.com/repos/mattrobenolt/heka/events","assignees_url":"https://api.github.com/repos/mattrobenolt/heka/assignees{/user}","branches_url":"https://api.github.com/repos/mattrobenolt/heka/branches{/branch}","tags_url":"https://api.github.com/repos/mattrobenolt/heka/tags","blobs_url":"https://api.github.com/repos/mattrobenolt/heka/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mattrobenolt/heka/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mattrobenolt/heka/git/refs{/sha}","trees_url":"https://api.github.com/repos/mattrobenolt/heka/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mattrobenolt/heka/statuses/{sha}","languages_url":"https://api.github.com/repos/mattrobenolt/heka/languages","stargazers_url":"https://api.github.com/repos/mattrobenolt/heka/stargazers","contributors_url":"https://api.github.com/repos/mattrobenolt/heka/contributors","subscribers_url":"https://api.github.com/repos/mattrobenolt/heka/subscribers","subscription_url":"https://api.github.com/repos/mattrobenolt/heka/subscription","commits_url":"https://api.github.com/repos/mattrobenolt/heka/commits{/sha}","git_commits_url":"https://api.github.com/repos/mattrobenolt/heka/git/commits{/sha}","comments_url":"https://api.github.com/repos/mattrobenolt/heka/comments{/number}","issue_comment_url":"https://api.github.com/repos/mattrobenolt/heka/issues/comments{/number}","contents_url":"https://api.github.com/repos/mattrobenolt/heka/contents/{+path}","compare_url":"https://api.github.com/repos/mattrobenolt/heka/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mattrobenolt/heka/merges","archive_url":"https://api.github.com/repos/mattrobenolt/heka/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mattrobenolt/heka/downloads","issues_url":"https://api.github.com/repos/mattrobenolt/heka/issues{/number}","pulls_url":"https://api.github.com/repos/mattrobenolt/heka/pulls{/number}","milestones_url":"https://api.github.com/repos/mattrobenolt/heka/milestones{/number}","notifications_url":"https://api.github.com/repos/mattrobenolt/heka/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mattrobenolt/heka/labels{/name}","releases_url":"https://api.github.com/repos/mattrobenolt/heka/releases{/id}","created_at":"2015-06-09T05:26:49Z","updated_at":"2015-06-09T05:26:51Z","pushed_at":"2015-06-10T19:19:41Z","git_url":"git://github.com/mattrobenolt/heka.git","ssh_url":"git@github.com:mattrobenolt/heka.git","clone_url":"https://github.com/mattrobenolt/heka.git","svn_url":"https://github.com/mattrobenolt/heka","homepage":"http://hekad.readthedocs.org/","size":11026,"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":"dev"}},"base":{"label":"mozilla-services:dev","ref":"dev","sha":"62e8282c682e3a358b8f0836ee6e72230c880b0b","user":{"login":"mozilla-services","id":1066228,"avatar_url":"https://avatars.githubusercontent.com/u/1066228?v=3","gravatar_id":"","url":"https://api.github.com/users/mozilla-services","html_url":"https://github.com/mozilla-services","followers_url":"https://api.github.com/users/mozilla-services/followers","following_url":"https://api.github.com/users/mozilla-services/following{/other_user}","gists_url":"https://api.github.com/users/mozilla-services/gists{/gist_id}","starred_url":"https://api.github.com/users/mozilla-services/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mozilla-services/subscriptions","organizations_url":"https://api.github.com/users/mozilla-services/orgs","repos_url":"https://api.github.com/users/mozilla-services/repos","events_url":"https://api.github.com/users/mozilla-services/events{/privacy}","received_events_url":"https://api.github.com/users/mozilla-services/received_events","type":"Organization","site_admin":false},"repo":{"id":6248212,"name":"heka","full_name":"mozilla-services/heka","owner":{"login":"mozilla-services","id":1066228,"avatar_url":"https://avatars.githubusercontent.com/u/1066228?v=3","gravatar_id":"","url":"https://api.github.com/users/mozilla-services","html_url":"https://github.com/mozilla-services","followers_url":"https://api.github.com/users/mozilla-services/followers","following_url":"https://api.github.com/users/mozilla-services/following{/other_user}","gists_url":"https://api.github.com/users/mozilla-services/gists{/gist_id}","starred_url":"https://api.github.com/users/mozilla-services/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mozilla-services/subscriptions","organizations_url":"https://api.github.com/users/mozilla-services/orgs","repos_url":"https://api.github.com/users/mozilla-services/repos","events_url":"https://api.github.com/users/mozilla-services/events{/privacy}","received_events_url":"https://api.github.com/users/mozilla-services/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mozilla-services/heka","description":"Data collection and processing made easy.","fork":false,"url":"https://api.github.com/repos/mozilla-services/heka","forks_url":"https://api.github.com/repos/mozilla-services/heka/forks","keys_url":"https://api.github.com/repos/mozilla-services/heka/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mozilla-services/heka/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mozilla-services/heka/teams","hooks_url":"https://api.github.com/repos/mozilla-services/heka/hooks","issue_events_url":"https://api.github.com/repos/mozilla-services/heka/issues/events{/number}","events_url":"https://api.github.com/repos/mozilla-services/heka/events","assignees_url":"https://api.github.com/repos/mozilla-services/heka/assignees{/user}","branches_url":"https://api.github.com/repos/mozilla-services/heka/branches{/branch}","tags_url":"https://api.github.com/repos/mozilla-services/heka/tags","blobs_url":"https://api.github.com/repos/mozilla-services/heka/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mozilla-services/heka/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mozilla-services/heka/git/refs{/sha}","trees_url":"https://api.github.com/repos/mozilla-services/heka/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mozilla-services/heka/statuses/{sha}","languages_url":"https://api.github.com/repos/mozilla-services/heka/languages","stargazers_url":"https://api.github.com/repos/mozilla-services/heka/stargazers","contributors_url":"https://api.github.com/repos/mozilla-services/heka/contributors","subscribers_url":"https://api.github.com/repos/mozilla-services/heka/subscribers","subscription_url":"https://api.github.com/repos/mozilla-services/heka/subscription","commits_url":"https://api.github.com/repos/mozilla-services/heka/commits{/sha}","git_commits_url":"https://api.github.com/repos/mozilla-services/heka/git/commits{/sha}","comments_url":"https://api.github.com/repos/mozilla-services/heka/comments{/number}","issue_comment_url":"https://api.github.com/repos/mozilla-services/heka/issues/comments{/number}","contents_url":"https://api.github.com/repos/mozilla-services/heka/contents/{+path}","compare_url":"https://api.github.com/repos/mozilla-services/heka/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mozilla-services/heka/merges","archive_url":"https://api.github.com/repos/mozilla-services/heka/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mozilla-services/heka/downloads","issues_url":"https://api.github.com/repos/mozilla-services/heka/issues{/number}","pulls_url":"https://api.github.com/repos/mozilla-services/heka/pulls{/number}","milestones_url":"https://api.github.com/repos/mozilla-services/heka/milestones{/number}","notifications_url":"https://api.github.com/repos/mozilla-services/heka/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mozilla-services/heka/labels{/name}","releases_url":"https://api.github.com/repos/mozilla-services/heka/releases{/id}","created_at":"2012-10-16T17:20:51Z","updated_at":"2015-06-20T00:05:19Z","pushed_at":"2015-06-19T23:31:12Z","git_url":"git://github.com/mozilla-services/heka.git","ssh_url":"git@github.com:mozilla-services/heka.git","clone_url":"https://github.com/mozilla-services/heka.git","svn_url":"https://github.com/mozilla-services/heka","homepage":"http://hekad.readthedocs.org/","size":19891,"stargazers_count":2286,"watchers_count":2286,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":291,"mirror_url":null,"open_issues_count":146,"forks":291,"open_issues":146,"watchers":2286,"default_branch":"dev"}},"_links":{"self":{"href":"https://api.github.com/repos/mozilla-services/heka/pulls/1570"},"html":{"href":"https://github.com/mozilla-services/heka/pull/1570"},"issue":{"href":"https://api.github.com/repos/mozilla-services/heka/issues/1570"},"comments":{"href":"https://api.github.com/repos/mozilla-services/heka/issues/1570/comments"},"review_comments":{"href":"https://api.github.com/repos/mozilla-services/heka/pulls/1570/comments"},"review_comment":{"href":"https://api.github.com/repos/mozilla-services/heka/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/mozilla-services/heka/pulls/1570/commits"},"statuses":{"href":"https://api.github.com/repos/mozilla-services/heka/statuses/715202094891b7c26987f16048e59e79effe9ef5"}}}}