[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class HarbormasterBuildLog extends HarbormasterDAO 4 implements PhabricatorPolicyInterface { 5 6 protected $buildTargetPHID; 7 protected $logSource; 8 protected $logType; 9 protected $duration; 10 protected $live; 11 12 private $buildTarget = self::ATTACHABLE; 13 14 const CHUNK_BYTE_LIMIT = 102400; 15 16 /** 17 * The log is encoded as plain text. 18 */ 19 const ENCODING_TEXT = 'text'; 20 21 public static function initializeNewBuildLog( 22 HarbormasterBuildTarget $build_target) { 23 24 return id(new HarbormasterBuildLog()) 25 ->setBuildTargetPHID($build_target->getPHID()) 26 ->setDuration(null) 27 ->setLive(0); 28 } 29 30 public function getConfiguration() { 31 return array( 32 self::CONFIG_AUX_PHID => true, 33 self::CONFIG_COLUMN_SCHEMA => array( 34 // T6203/NULLABILITY 35 // It seems like these should be non-nullable? All logs should have a 36 // source, etc. 37 'logSource' => 'text255?', 38 'logType' => 'text255?', 39 'duration' => 'uint32?', 40 41 'live' => 'bool', 42 ), 43 self::CONFIG_KEY_SCHEMA => array( 44 'key_buildtarget' => array( 45 'columns' => array('buildTargetPHID'), 46 ), 47 ), 48 ) + parent::getConfiguration(); 49 } 50 51 public function generatePHID() { 52 return PhabricatorPHID::generateNewPHID( 53 HarbormasterBuildLogPHIDType::TYPECONST); 54 } 55 56 public function attachBuildTarget(HarbormasterBuildTarget $build_target) { 57 $this->buildTarget = $build_target; 58 return $this; 59 } 60 61 public function getBuildTarget() { 62 return $this->assertAttached($this->buildTarget); 63 } 64 65 public function getName() { 66 return pht('Build Log'); 67 } 68 69 public function start() { 70 if ($this->getLive()) { 71 throw new Exception('Live logging has already started for this log.'); 72 } 73 74 $this->setLive(1); 75 $this->save(); 76 77 return time(); 78 } 79 80 public function append($content) { 81 if (!$this->getLive()) { 82 throw new Exception('Start logging before appending data to the log.'); 83 } 84 if (strlen($content) === 0) { 85 return; 86 } 87 88 // If the length of the content is greater than the chunk size limit, 89 // then we can never fit the content in a single record. We need to 90 // split our content out and call append on it for as many parts as there 91 // are to the content. 92 if (strlen($content) > self::CHUNK_BYTE_LIMIT) { 93 $current = $content; 94 while (strlen($current) > self::CHUNK_BYTE_LIMIT) { 95 $part = substr($current, 0, self::CHUNK_BYTE_LIMIT); 96 $current = substr($current, self::CHUNK_BYTE_LIMIT); 97 $this->append($part); 98 } 99 $this->append($current); 100 return; 101 } 102 103 // Retrieve the size of last chunk from the DB for this log. If the 104 // chunk is over 500K, then we need to create a new log entry. 105 $conn = $this->establishConnection('w'); 106 $result = queryfx_all( 107 $conn, 108 'SELECT id, size, encoding '. 109 'FROM harbormaster_buildlogchunk '. 110 'WHERE logID = %d '. 111 'ORDER BY id DESC '. 112 'LIMIT 1', 113 $this->getID()); 114 if (count($result) === 0 || 115 $result[0]['size'] + strlen($content) > self::CHUNK_BYTE_LIMIT || 116 $result[0]['encoding'] !== self::ENCODING_TEXT) { 117 118 // We must insert a new chunk because the data we are appending 119 // won't fit into the existing one, or we don't have any existing 120 // chunk data. 121 queryfx( 122 $conn, 123 'INSERT INTO harbormaster_buildlogchunk '. 124 '(logID, encoding, size, chunk) '. 125 'VALUES '. 126 '(%d, %s, %d, %s)', 127 $this->getID(), 128 self::ENCODING_TEXT, 129 strlen($content), 130 $content); 131 } else { 132 // We have a resulting record that we can append our content onto. 133 queryfx( 134 $conn, 135 'UPDATE harbormaster_buildlogchunk '. 136 'SET chunk = CONCAT(chunk, %s), size = LENGTH(CONCAT(chunk, %s))'. 137 'WHERE id = %d', 138 $content, 139 $content, 140 $result[0]['id']); 141 } 142 } 143 144 public function finalize($start = 0) { 145 if (!$this->getLive()) { 146 throw new Exception('Start logging before finalizing it.'); 147 } 148 149 // TODO: Encode the log contents in a gzipped format. 150 $this->reload(); 151 if ($start > 0) { 152 $this->setDuration(time() - $start); 153 } 154 $this->setLive(0); 155 $this->save(); 156 } 157 158 public function getLogText() { 159 // TODO: This won't cope very well if we're pulling like a 700MB 160 // log file out of the DB. We should probably implement some sort 161 // of optional limit parameter so that when we're rendering out only 162 // 25 lines in the UI, we don't wastefully read in the whole log. 163 164 // We have to read our content out of the database and stitch all of 165 // the log data back together. 166 $conn = $this->establishConnection('r'); 167 $result = queryfx_all( 168 $conn, 169 'SELECT chunk '. 170 'FROM harbormaster_buildlogchunk '. 171 'WHERE logID = %d '. 172 'ORDER BY id ASC', 173 $this->getID()); 174 175 $content = ''; 176 foreach ($result as $row) { 177 $content .= $row['chunk']; 178 } 179 return $content; 180 } 181 182 183 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 184 185 186 public function getCapabilities() { 187 return array( 188 PhabricatorPolicyCapability::CAN_VIEW, 189 ); 190 } 191 192 public function getPolicy($capability) { 193 return $this->getBuildTarget()->getPolicy($capability); 194 } 195 196 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 197 return $this->getBuildTarget()->hasAutomaticCapability( 198 $capability, 199 $viewer); 200 } 201 202 public function describeAutomaticCapability($capability) { 203 return pht( 204 'Users must be able to see a build target to view it\'s build log.'); 205 } 206 207 208 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |