diff --git a/library/Graphite/Graphing/Chart.php b/library/Graphite/Graphing/Chart.php new file mode 100644 index 0000000..75f6659 --- /dev/null +++ b/library/Graphite/Graphing/Chart.php @@ -0,0 +1,304 @@ +graphiteWebClient = $graphiteWebClient; + $this->template = $template; + $this->metrics = $metrics; + } + + /** + * Let Graphite Web render this chart and serve the result immediately to the user agent (via the given response) + * + * Does not return. + * + * @param Response $response + */ + public function serveImage(Response $response) + { + $params = (new UrlParams())->addValues([ + 'from' => $this->from, + 'width' => $this->width, + 'height' => $this->height, + 'hideLegend' => (string) ! $this->showLegend, + 'tz' => $this->timeZone->getName(), + '_salt' => time() . '.000', + 'hideGrid' => 'true', + 'vTitle' => 'Percent', + 'lineMode' => 'connected', + 'xFormat' => '%a %H:%M', + 'drawNullAsZero' => 'false', + 'graphType' => 'line', + '_ext' => 'whatever.svg' + ]); + + if ($this->until !== null) { + $params->set('until', $this->until); + } + + $variables = $this->getMetricVariables(); + + foreach ($this->template->getUrlParams() as $key => $value) { + $params->set($key, $value->resolve($variables)); + } + + $image = $this->graphiteWebClient->request('/render', $params, 'POST', [ + 'Accept-language' => 'en', + 'Content-type' => 'application/x-www-form-urlencoded' + ]); + + $response + ->setHeader('Content-Type', 'image/png', true) + ->setHeader('Content-Disposition', 'inline; filename="graph.png"', true) + ->setBody($image) + ->sendResponse(); + + exit; + } + + /** + * Extract the values of the template's metrics filters' variables from the target metrics + * + * @return string[] + */ + public function getMetricVariables() + { + /** @var MacroTemplate[][] $curves */ + $curves = $this->template->getCurves(); + $variables = []; + + foreach ($this->metrics as $curveName => $metric) { + $vars = $curves[$curveName][0]->reverseResolve($metric); + if ($vars !== false) { + $variables = array_merge($variables, $vars); + } + } + + return $variables; + } + + /** + * Get begin + * + * @return string + */ + public function getFrom() + { + return $this->from; + } + + /** + * Set begin + * + * @param string $from + * + * @return $this + */ + public function setFrom($from) + { + $this->from = $from; + + return $this; + } + + /** + * Get end + * + * @return string + */ + public function getUntil() + { + return $this->until; + } + + /** + * Set end + * + * @param string $until + * + * @return $this + */ + public function setUntil($until) + { + $this->until = $until; + + return $this; + } + + /** + * Get width + * + * @return int + */ + public function getWidth() + { + return $this->width; + } + + /** + * Set width + * + * @param int $width + * + * @return $this + */ + public function setWidth($width) + { + $this->width = $width; + + return $this; + } + + /** + * Get height + * + * @return int + */ + public function getHeight() + { + return $this->height; + } + + /** + * Set height + * + * @param int $height + * + * @return $this + */ + public function setHeight($height) + { + $this->height = $height; + + return $this; + } + + /** + * Get whether to show the chart's legend + * + * @return bool + */ + public function getShowLegend() + { + return $this->showLegend; + } + + /** + * Set whether to show the chart's legend + * + * @param bool $showLegend + * + * @return $this + */ + public function setShowLegend($showLegend) + { + $this->showLegend = $showLegend; + + return $this; + } + + /** + * Get time zone + * + * @return DateTimeZone + */ + public function getTimeZone() + { + return $this->timeZone; + } + + /** + * Set time zone + * + * @param DateTimeZone $timeZone + * + * @return $this + */ + public function setTimeZone(DateTimeZone $timeZone) + { + $this->timeZone = $timeZone; + + return $this; + } +} diff --git a/library/Graphite/Graphing/Template.php b/library/Graphite/Graphing/Template.php index c51e64b..41ae992 100644 --- a/library/Graphite/Graphing/Template.php +++ b/library/Graphite/Graphing/Template.php @@ -31,6 +31,101 @@ class Template { } + /** + * Get all charts based on this template and applicable to the metrics + * from the given data source restricted by the given filter + * + * @param MetricsDataSource $dataSource + * @param string[] $filter + * + * @return Chart[] + */ + public function getCharts(MetricsDataSource $dataSource, array $filter) + { + $metrics = []; + foreach ($this->curves as $curveName => $curve) { + $query = $dataSource->select()->from($curve[0]); + foreach ($filter as $key => $value) { + $query->where($key, $value); + } + + foreach ($query->fetchColumn() as $metric) { + $vars = $curve[0]->reverseResolve($metric); + if ($vars !== false) { + $metrics[$curveName][$metric] = $vars; + } + } + } + + if (empty($metrics)) { + return []; + } + + $intersectingVariables = []; + foreach ($metrics as $curveName1 => $_) { + foreach ($metrics as $curveName2 => $_) { + if ($curveName1 !== $curveName2 && ! isset($intersectingVariables[$curveName2][$curveName1])) { + $vars = array_intersect( + $this->curves[$curveName1][0]->getMacros(), + $this->curves[$curveName2][0]->getMacros() + ); + if (! empty($vars)) { + $intersectingVariables[$curveName1][$curveName2] = $vars; + } + } + } + } + + $iterState = []; + foreach ($metrics as $curveName => $metric) { + $iterState[$curveName] = [0, array_keys($metric)]; + } + + $metricsCombinations = []; + $currentMetrics = []; + do { + foreach ($metrics as $curveName => $metric) { + $currentMetrics[$curveName] = $iterState[$curveName][1][ $iterState[$curveName][0] ]; + } + + $acceptCombination = true; + foreach ($intersectingVariables as $curveName1 => $intersectingWith) { + foreach ($intersectingWith as $curveName2 => $vars) { + foreach ($vars as $key) { + if ($metrics[$curveName1][ $currentMetrics[$curveName1] ][$key] + !== $metrics[$curveName2][ $currentMetrics[$curveName2] ][$key]) { + $acceptCombination = false; + break 3; + } + } + } + } + + if ($acceptCombination) { + $metricsCombinations[] = $currentMetrics; + } + + $overflow = true; + foreach ($iterState as $curveName => & $iterSubState) { + if (isset($iterSubState[1][ ++$iterSubState[0] ])) { + $overflow = false; + break; + } else { + $iterSubState[0] = 0; + } + } + + unset($iterSubState); + } while (! $overflow); + + $charts = []; + foreach ($metricsCombinations as $metricsCombination) { + $charts[] = new Chart($dataSource->getClient(), $this, $metricsCombination); + } + + return $charts; + } + /** * Get curves to show in a chart by name with Graphite Web metric filters and Graphite functions *