[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/harbormaster/storage/build/ -> HarbormasterBuildLog.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1