{"action":"created","comment":{"url":"https://api.github.com/repos/coreos/etcd/pulls/comments/43550975","id":43550975,"diff_hunk":"@@ -0,0 +1,170 @@\n+// Copyright 2015 CoreOS, Inc.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+package logutil\n+\n+import (\n+\t\"fmt\"\n+\t\"sync\"\n+\t\"time\"\n+\n+\t\"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog\"\n+)\n+\n+// line represents a log line that can be printed out\n+// through capnslog.PackageLogger.\n+type line struct {\n+\tlevel capnslog.LogLevel\n+\tstr   string\n+}\n+\n+type status struct {\n+\tgap        time.Duration\n+\tprintTimer *time.Timer\n+\n+\tmu    sync.Mutex // protect fields below\n+\tstart time.Time  // time to start merging\n+\tcount int        // number of lines from start field\n+}\n+\n+// MergeLogger merges repeated log lines and prints summary log lines.\n+// It doesn't merge log at DEBUG and TRACE level because they should\n+// be verbosal.\n+//\n+// For one log line, MergeLogger prints out the line when the line appears\n+// at the first time. Then it holds and merges lines with the same\n+// string. It stops merging when the line doesn't appear within one merge gap.\n+// It prints out summary log line when merge is stopped.\n+//\n+// MergeLogger prints out summary log line for all log lines every period.\n+type MergeLogger struct {\n+\t*capnslog.PackageLogger\n+\tGap    time.Duration\n+\tPeriod time.Duration\n+\n+\tmu      sync.Mutex // protect statusm\n+\tstatusm map[line]*status\n+}\n+\n+func (l *MergeLogger) Info(entries ...interface{}) {\n+\tl.merge(line{\n+\t\tlevel: capnslog.INFO,\n+\t\tstr:   fmt.Sprint(entries...),\n+\t})\n+}\n+\n+func (l *MergeLogger) Infof(format string, args ...interface{}) {\n+\tl.merge(line{\n+\t\tlevel: capnslog.INFO,\n+\t\tstr:   fmt.Sprintf(format, args...),\n+\t})\n+}\n+\n+func (l *MergeLogger) Notice(entries ...interface{}) {\n+\tl.merge(line{\n+\t\tlevel: capnslog.NOTICE,\n+\t\tstr:   fmt.Sprint(entries...),\n+\t})\n+}\n+\n+func (l *MergeLogger) Noticef(format string, args ...interface{}) {\n+\tl.merge(line{\n+\t\tlevel: capnslog.NOTICE,\n+\t\tstr:   fmt.Sprintf(format, args...),\n+\t})\n+}\n+\n+func (l *MergeLogger) Warning(entries ...interface{}) {\n+\tl.merge(line{\n+\t\tlevel: capnslog.WARNING,\n+\t\tstr:   fmt.Sprint(entries...),\n+\t})\n+}\n+\n+func (l *MergeLogger) Warningf(format string, args ...interface{}) {\n+\tl.merge(line{\n+\t\tlevel: capnslog.WARNING,\n+\t\tstr:   fmt.Sprintf(format, args...),\n+\t})\n+}\n+\n+func (l *MergeLogger) Error(entries ...interface{}) {\n+\tl.merge(line{\n+\t\tlevel: capnslog.ERROR,\n+\t\tstr:   fmt.Sprint(entries...),\n+\t})\n+}\n+\n+func (l *MergeLogger) Errorf(format string, args ...interface{}) {\n+\tl.merge(line{\n+\t\tlevel: capnslog.ERROR,\n+\t\tstr:   fmt.Sprintf(format, args...),\n+\t})\n+}\n+\n+func (l *MergeLogger) merge(ln line) {\n+\tl.mu.Lock()\n+\tdefer l.mu.Unlock()\n+\n+\t// initialize logger\n+\tif l.statusm == nil {\n+\t\tl.statusm = make(map[line]*status)\n+\t\tgo l.printAllLoop()\n+\t}\n+\n+\t// refresh timer if the logger is merging the line\n+\tif status, ok := l.statusm[ln]; ok {\n+\t\tif status.printTimer.Reset(status.gap) {\n+\t\t\tstatus.mu.Lock()\n+\t\t\tstatus.count++\n+\t\t\tstatus.mu.Unlock()\n+\t\t\treturn\n+\t\t}\n+\t}\n+\n+\t// print out the line at the first time\n+\tl.PackageLogger.Logf(ln.level, ln.str)\n+\t// initialize status of the line\n+\tstatus := &status{\n+\t\tstart: time.Now(),\n+\t\tgap:   l.Gap,\n+\t}\n+\tstatus.printTimer = time.AfterFunc(status.gap, func() {\n+\t\tprint(l.PackageLogger, ln, status)\n+\t})\n+\tl.statusm[ln] = status\n+}\n+\n+func (l *MergeLogger) printAllLoop() {","path":"pkg/logutil/merge_logger.go","position":149,"original_position":149,"commit_id":"1671d71ab8c022ba940ec39c317a41fb2ba8332e","original_commit_id":"1671d71ab8c022ba940ec39c317a41fb2ba8332e","user":{"login":"xiang90","id":4479947,"avatar_url":"https://avatars.githubusercontent.com/u/4479947?v=3","gravatar_id":"","url":"https://api.github.com/users/xiang90","html_url":"https://github.com/xiang90","followers_url":"https://api.github.com/users/xiang90/followers","following_url":"https://api.github.com/users/xiang90/following{/other_user}","gists_url":"https://api.github.com/users/xiang90/gists{/gist_id}","starred_url":"https://api.github.com/users/xiang90/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xiang90/subscriptions","organizations_url":"https://api.github.com/users/xiang90/orgs","repos_url":"https://api.github.com/users/xiang90/repos","events_url":"https://api.github.com/users/xiang90/events{/privacy}","received_events_url":"https://api.github.com/users/xiang90/received_events","type":"User","site_admin":false},"body":"outputLoop?","created_at":"2015-10-30T20:53:32Z","updated_at":"2015-10-30T20:53:32Z","html_url":"https://github.com/coreos/etcd/pull/3783#discussion_r43550975","pull_request_url":"https://api.github.com/repos/coreos/etcd/pulls/3783","_links":{"self":{"href":"https://api.github.com/repos/coreos/etcd/pulls/comments/43550975"},"html":{"href":"https://github.com/coreos/etcd/pull/3783#discussion_r43550975"},"pull_request":{"href":"https://api.github.com/repos/coreos/etcd/pulls/3783"}}},"pull_request":{"url":"https://api.github.com/repos/coreos/etcd/pulls/3783","id":49320336,"html_url":"https://github.com/coreos/etcd/pull/3783","diff_url":"https://github.com/coreos/etcd/pull/3783.diff","patch_url":"https://github.com/coreos/etcd/pull/3783.patch","issue_url":"https://api.github.com/repos/coreos/etcd/issues/3783","number":3783,"state":"open","locked":false,"title":"rafthttp: use MergeLogger for rafthttp logging","user":{"login":"yichengq","id":2976232,"avatar_url":"https://avatars.githubusercontent.com/u/2976232?v=3","gravatar_id":"","url":"https://api.github.com/users/yichengq","html_url":"https://github.com/yichengq","followers_url":"https://api.github.com/users/yichengq/followers","following_url":"https://api.github.com/users/yichengq/following{/other_user}","gists_url":"https://api.github.com/users/yichengq/gists{/gist_id}","starred_url":"https://api.github.com/users/yichengq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yichengq/subscriptions","organizations_url":"https://api.github.com/users/yichengq/orgs","repos_url":"https://api.github.com/users/yichengq/repos","events_url":"https://api.github.com/users/yichengq/events{/privacy}","received_events_url":"https://api.github.com/users/yichengq/received_events","type":"User","site_admin":false},"body":"rafthttp logs repeated messages whenever the same connection error\r\nhappens, and it easily becomes log spamming.\r\nUse MergeLogger as the default logger to merge repeated log lines.\r\n\r\nThis performs the same functionality as logging control in peerStatus.\r\nSo I remove the logging control in peerStatus and only use MergeLogger.\r\n\r\nA leader logs like this:\r\n```\r\n2015-10-30 13:10:50.692914 E | rafthttp: failed to dial 91bc3c398fb3c146 on stream Message (dial tcp 127.0.0.1:22380: getsockopt: connection refused)\r\n2015-10-30 13:10:50.692960 E | rafthttp: failed to dial 91bc3c398fb3c146 on stream MsgApp v2 (dial tcp 127.0.0.1:22380: getsockopt: connection refused)\r\n2015-10-30 13:10:50.708507 E | rafthttp: failed to dial fd422379fda50e48 on stream Message (dial tcp 127.0.0.1:32380: getsockopt: connection refused)\r\n2015-10-30 13:10:50.708538 E | rafthttp: failed to dial fd422379fda50e48 on stream MsgApp v2 (dial tcp 127.0.0.1:32380: getsockopt: connection refused)\r\n...\r\n[one follow is online]\r\n2015-10-30 13:10:58.689713 E | rafthttp: failed to write 91bc3c398fb3c146 on pipeline (dial tcp 127.0.0.1:22380: getsockopt: connection refused) [merged 2 repeated lines in 7.399797956s]\r\n2015-10-30 13:10:59.233615 E | rafthttp: failed to dial 91bc3c398fb3c146 on stream Message (dial tcp 127.0.0.1:22380: getsockopt: connection refused) [merged 35 repeated lines in 8.540639729s]\r\n2015-10-30 13:10:59.261046 E | rafthttp: failed to dial 91bc3c398fb3c146 on stream MsgApp v2 (dial tcp 127.0.0.1:22380: getsockopt: connection refused) [merged 35 repeated lines in 8.567979554s]\r\n...\r\n[wait 30s]\r\n2015-10-30 13:11:20.693268 E | rafthttp: failed to dial fd422379fda50e48 on stream Message (dial tcp 127.0.0.1:32380: getsockopt: connection refused) [merged 295 repeated lines in 29.984732966s]\r\n2015-10-30 13:11:20.693303 E | rafthttp: failed to dial fd422379fda50e48 on stream MsgApp v2 (dial tcp 127.0.0.1:32380: getsockopt: connection refused) [merged 295 repeated lines in 29.984761119s]\r\n2015-10-30 13:11:20.693315 E | rafthttp: failed to write fd422379fda50e48 on pipeline (dial tcp 127.0.0.1:32380: getsockopt: connection refused) [merged 320 repeated lines in 29.403499857s]\r\n...\r\n[another follower is online]\r\n2015-10-30 13:11:35.328399 I | rafthttp: the connection with fd422379fda50e48 became active\r\n2015-10-30 13:11:40.230160 E | rafthttp: failed to dial fd422379fda50e48 on stream Message (dial tcp 127.0.0.1:32380: getsockopt: connection refused) [merged 144 repeated lines in 19.536887999s]\r\n2015-10-30 13:11:40.230191 E | rafthttp: failed to dial fd422379fda50e48 on stream MsgApp v2 (dial tcp 127.0.0.1:32380: getsockopt: connection refused) [merged 144 repeated lines in 19.536834025s]\r\n2015-10-30 13:11:40.288879 E | rafthttp: failed to write fd422379fda50e48 on pipeline (dial tcp 127.0.0.1:32380: getsockopt: connection refused) [merged 181 repeated lines in 19.595536347s]\r\n```\r\n\r\nA slow-snapshot-recovery follower acts like this:\r\n```\r\n2015-10-30 13:11:45.414857 W | rafthttp: dropped MsgAppResp to 8211f1d0f64f3269 since pipeline's sending buffer is full\r\n2015-10-30 13:11:45.414923 W | rafthttp: dropped MsgHeartbeatResp to 8211f1d0f64f3269 since pipeline's sending buffer is full\r\n...\r\n2015-10-30 13:11:50.417285 W | rafthttp: dropped MsgHeartbeatResp to 8211f1d0f64f3269 since pipeline's sending buffer is full [merged 37 repeated lines in 5.002306239s]\r\n2015-10-30 13:11:50.423570 W | rafthttp: dropped MsgAppResp to 8211f1d0f64f3269 since pipeline's sending buffer is full [merged 21 repeated lines in 5.008680629s]\r\n```\r\n\r\nfixes #3462 in a general way","created_at":"2015-10-30T20:45:27Z","updated_at":"2015-10-30T20:53:32Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d46cbe2c40effff8aa1bff72811b242796f34477","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/coreos/etcd/pulls/3783/commits","review_comments_url":"https://api.github.com/repos/coreos/etcd/pulls/3783/comments","review_comment_url":"https://api.github.com/repos/coreos/etcd/pulls/comments{/number}","comments_url":"https://api.github.com/repos/coreos/etcd/issues/3783/comments","statuses_url":"https://api.github.com/repos/coreos/etcd/statuses/1671d71ab8c022ba940ec39c317a41fb2ba8332e","head":{"label":"yichengq:merge-logger","ref":"merge-logger","sha":"1671d71ab8c022ba940ec39c317a41fb2ba8332e","user":{"login":"yichengq","id":2976232,"avatar_url":"https://avatars.githubusercontent.com/u/2976232?v=3","gravatar_id":"","url":"https://api.github.com/users/yichengq","html_url":"https://github.com/yichengq","followers_url":"https://api.github.com/users/yichengq/followers","following_url":"https://api.github.com/users/yichengq/following{/other_user}","gists_url":"https://api.github.com/users/yichengq/gists{/gist_id}","starred_url":"https://api.github.com/users/yichengq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yichengq/subscriptions","organizations_url":"https://api.github.com/users/yichengq/orgs","repos_url":"https://api.github.com/users/yichengq/repos","events_url":"https://api.github.com/users/yichengq/events{/privacy}","received_events_url":"https://api.github.com/users/yichengq/received_events","type":"User","site_admin":false},"repo":{"id":16744058,"name":"etcd","full_name":"yichengq/etcd","owner":{"login":"yichengq","id":2976232,"avatar_url":"https://avatars.githubusercontent.com/u/2976232?v=3","gravatar_id":"","url":"https://api.github.com/users/yichengq","html_url":"https://github.com/yichengq","followers_url":"https://api.github.com/users/yichengq/followers","following_url":"https://api.github.com/users/yichengq/following{/other_user}","gists_url":"https://api.github.com/users/yichengq/gists{/gist_id}","starred_url":"https://api.github.com/users/yichengq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yichengq/subscriptions","organizations_url":"https://api.github.com/users/yichengq/orgs","repos_url":"https://api.github.com/users/yichengq/repos","events_url":"https://api.github.com/users/yichengq/events{/privacy}","received_events_url":"https://api.github.com/users/yichengq/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/yichengq/etcd","description":"A highly-available key value store for shared configuration and service discovery","fork":true,"url":"https://api.github.com/repos/yichengq/etcd","forks_url":"https://api.github.com/repos/yichengq/etcd/forks","keys_url":"https://api.github.com/repos/yichengq/etcd/keys{/key_id}","collaborators_url":"https://api.github.com/repos/yichengq/etcd/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/yichengq/etcd/teams","hooks_url":"https://api.github.com/repos/yichengq/etcd/hooks","issue_events_url":"https://api.github.com/repos/yichengq/etcd/issues/events{/number}","events_url":"https://api.github.com/repos/yichengq/etcd/events","assignees_url":"https://api.github.com/repos/yichengq/etcd/assignees{/user}","branches_url":"https://api.github.com/repos/yichengq/etcd/branches{/branch}","tags_url":"https://api.github.com/repos/yichengq/etcd/tags","blobs_url":"https://api.github.com/repos/yichengq/etcd/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/yichengq/etcd/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/yichengq/etcd/git/refs{/sha}","trees_url":"https://api.github.com/repos/yichengq/etcd/git/trees{/sha}","statuses_url":"https://api.github.com/repos/yichengq/etcd/statuses/{sha}","languages_url":"https://api.github.com/repos/yichengq/etcd/languages","stargazers_url":"https://api.github.com/repos/yichengq/etcd/stargazers","contributors_url":"https://api.github.com/repos/yichengq/etcd/contributors","subscribers_url":"https://api.github.com/repos/yichengq/etcd/subscribers","subscription_url":"https://api.github.com/repos/yichengq/etcd/subscription","commits_url":"https://api.github.com/repos/yichengq/etcd/commits{/sha}","git_commits_url":"https://api.github.com/repos/yichengq/etcd/git/commits{/sha}","comments_url":"https://api.github.com/repos/yichengq/etcd/comments{/number}","issue_comment_url":"https://api.github.com/repos/yichengq/etcd/issues/comments{/number}","contents_url":"https://api.github.com/repos/yichengq/etcd/contents/{+path}","compare_url":"https://api.github.com/repos/yichengq/etcd/compare/{base}...{head}","merges_url":"https://api.github.com/repos/yichengq/etcd/merges","archive_url":"https://api.github.com/repos/yichengq/etcd/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/yichengq/etcd/downloads","issues_url":"https://api.github.com/repos/yichengq/etcd/issues{/number}","pulls_url":"https://api.github.com/repos/yichengq/etcd/pulls{/number}","milestones_url":"https://api.github.com/repos/yichengq/etcd/milestones{/number}","notifications_url":"https://api.github.com/repos/yichengq/etcd/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/yichengq/etcd/labels{/name}","releases_url":"https://api.github.com/repos/yichengq/etcd/releases{/id}","created_at":"2014-02-11T20:18:23Z","updated_at":"2015-02-12T19:42:08Z","pushed_at":"2015-10-30T20:45:18Z","git_url":"git://github.com/yichengq/etcd.git","ssh_url":"git@github.com:yichengq/etcd.git","clone_url":"https://github.com/yichengq/etcd.git","svn_url":"https://github.com/yichengq/etcd","homepage":"","size":42953,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":false,"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":"coreos:master","ref":"master","sha":"2840260b3becbf108688ad865fe470a58eadaef5","user":{"login":"coreos","id":3730757,"avatar_url":"https://avatars.githubusercontent.com/u/3730757?v=3","gravatar_id":"","url":"https://api.github.com/users/coreos","html_url":"https://github.com/coreos","followers_url":"https://api.github.com/users/coreos/followers","following_url":"https://api.github.com/users/coreos/following{/other_user}","gists_url":"https://api.github.com/users/coreos/gists{/gist_id}","starred_url":"https://api.github.com/users/coreos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coreos/subscriptions","organizations_url":"https://api.github.com/users/coreos/orgs","repos_url":"https://api.github.com/users/coreos/repos","events_url":"https://api.github.com/users/coreos/events{/privacy}","received_events_url":"https://api.github.com/users/coreos/received_events","type":"Organization","site_admin":false},"repo":{"id":11225014,"name":"etcd","full_name":"coreos/etcd","owner":{"login":"coreos","id":3730757,"avatar_url":"https://avatars.githubusercontent.com/u/3730757?v=3","gravatar_id":"","url":"https://api.github.com/users/coreos","html_url":"https://github.com/coreos","followers_url":"https://api.github.com/users/coreos/followers","following_url":"https://api.github.com/users/coreos/following{/other_user}","gists_url":"https://api.github.com/users/coreos/gists{/gist_id}","starred_url":"https://api.github.com/users/coreos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coreos/subscriptions","organizations_url":"https://api.github.com/users/coreos/orgs","repos_url":"https://api.github.com/users/coreos/repos","events_url":"https://api.github.com/users/coreos/events{/privacy}","received_events_url":"https://api.github.com/users/coreos/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/coreos/etcd","description":"Distributed reliable key-value store for the most critical data of a distributed system","fork":false,"url":"https://api.github.com/repos/coreos/etcd","forks_url":"https://api.github.com/repos/coreos/etcd/forks","keys_url":"https://api.github.com/repos/coreos/etcd/keys{/key_id}","collaborators_url":"https://api.github.com/repos/coreos/etcd/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/coreos/etcd/teams","hooks_url":"https://api.github.com/repos/coreos/etcd/hooks","issue_events_url":"https://api.github.com/repos/coreos/etcd/issues/events{/number}","events_url":"https://api.github.com/repos/coreos/etcd/events","assignees_url":"https://api.github.com/repos/coreos/etcd/assignees{/user}","branches_url":"https://api.github.com/repos/coreos/etcd/branches{/branch}","tags_url":"https://api.github.com/repos/coreos/etcd/tags","blobs_url":"https://api.github.com/repos/coreos/etcd/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/coreos/etcd/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/coreos/etcd/git/refs{/sha}","trees_url":"https://api.github.com/repos/coreos/etcd/git/trees{/sha}","statuses_url":"https://api.github.com/repos/coreos/etcd/statuses/{sha}","languages_url":"https://api.github.com/repos/coreos/etcd/languages","stargazers_url":"https://api.github.com/repos/coreos/etcd/stargazers","contributors_url":"https://api.github.com/repos/coreos/etcd/contributors","subscribers_url":"https://api.github.com/repos/coreos/etcd/subscribers","subscription_url":"https://api.github.com/repos/coreos/etcd/subscription","commits_url":"https://api.github.com/repos/coreos/etcd/commits{/sha}","git_commits_url":"https://api.github.com/repos/coreos/etcd/git/commits{/sha}","comments_url":"https://api.github.com/repos/coreos/etcd/comments{/number}","issue_comment_url":"https://api.github.com/repos/coreos/etcd/issues/comments{/number}","contents_url":"https://api.github.com/repos/coreos/etcd/contents/{+path}","compare_url":"https://api.github.com/repos/coreos/etcd/compare/{base}...{head}","merges_url":"https://api.github.com/repos/coreos/etcd/merges","archive_url":"https://api.github.com/repos/coreos/etcd/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/coreos/etcd/downloads","issues_url":"https://api.github.com/repos/coreos/etcd/issues{/number}","pulls_url":"https://api.github.com/repos/coreos/etcd/pulls{/number}","milestones_url":"https://api.github.com/repos/coreos/etcd/milestones{/number}","notifications_url":"https://api.github.com/repos/coreos/etcd/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/coreos/etcd/labels{/name}","releases_url":"https://api.github.com/repos/coreos/etcd/releases{/id}","created_at":"2013-07-06T21:57:21Z","updated_at":"2015-10-30T18:38:59Z","pushed_at":"2015-10-30T20:45:27Z","git_url":"git://github.com/coreos/etcd.git","ssh_url":"git@github.com:coreos/etcd.git","clone_url":"https://github.com/coreos/etcd.git","svn_url":"https://github.com/coreos/etcd","homepage":"https://coreos.com/etcd/docs/latest/","size":38288,"stargazers_count":7884,"watchers_count":7884,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1181,"mirror_url":null,"open_issues_count":153,"forks":1181,"open_issues":153,"watchers":7884,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/coreos/etcd/pulls/3783"},"html":{"href":"https://github.com/coreos/etcd/pull/3783"},"issue":{"href":"https://api.github.com/repos/coreos/etcd/issues/3783"},"comments":{"href":"https://api.github.com/repos/coreos/etcd/issues/3783/comments"},"review_comments":{"href":"https://api.github.com/repos/coreos/etcd/pulls/3783/comments"},"review_comment":{"href":"https://api.github.com/repos/coreos/etcd/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/coreos/etcd/pulls/3783/commits"},"statuses":{"href":"https://api.github.com/repos/coreos/etcd/statuses/1671d71ab8c022ba940ec39c317a41fb2ba8332e"}}}}