Parcourir la source

StatsD service

Maximiliano Schvindt il y a 8 ans
Parent
commit
38d8d9490c

+ 38 - 0
Command/StatsDCommand.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace StatsDBundle\StatsDBundle\Command;
+
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class StatsDCommand extends ContainerAwareCommand
+{
+
+    protected function configure()
+    {
+        $this
+                ->setName('statsd:statsd')
+                ->setDescription('Insert metric in statsd service')
+                ->addArgument('metric', InputArgument::REQUIRED, 'Metric name')
+                ->addArgument('value', InputArgument::REQUIRED, 'Metric value')
+                ->addArgument('function', InputArgument::OPTIONAL, 'StatsD service function', 'gauge')
+        ;
+    }
+
+    /**
+     * @param InputInterface $input
+     * @param OutputInterface $output
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        $function = $input->getArgument('function');
+        $metric = $input->getArgument('metric');
+        $value = $input->getArgument('value');
+        $output->writeln("statsd: <info>{$function}</info> metric: <info>{$metric}</info> value: <info>{$value}</info>");
+        $statsdService = $this->getContainer()->get('statsd');
+        $statsdService->$function($metric, $value);
+    }
+
+}

+ 2 - 2
DependencyInjection/StatsDExtension.php

@@ -22,7 +22,7 @@ class StatsDExtension extends Extension
         $configuration = new Configuration();
         $config = $this->processConfiguration($configuration, $configs);
 
-        $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
-        $loader->load('services.xml');
+        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
+        $loader->load('services.yml');
     }
 }

+ 0 - 20
Resources/config/services.xml

@@ -1,20 +0,0 @@
-<?xml version="1.0" ?>
-
-<container xmlns="http://symfony.com/schema/dic/services"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
-
-    <!--
-    <parameters>
-        <parameter key="stats_d.example.class">StatsDBundle\StatsDBundle\Example</parameter>
-    </parameters>
-
-    <services>
-        <service id="stats_d.example" class="%stats_d.example.class%">
-            <argument type="service" id="service_id" />
-            <argument>plain_value</argument>
-            <argument>%parameter_name%</argument>
-        </service>
-    </services>
-    -->
-</container>

+ 3 - 0
Resources/config/services.yml

@@ -0,0 +1,3 @@
+services:
+    statsd:
+        class: StatsDBundle\StatsDBundle\Services\StatsD

+ 3 - 0
Resources/config/statsd.ini

@@ -0,0 +1,3 @@
+[statsd]
+host = 127.0.0.1
+port = 8125

+ 73 - 0
Services/Config.php

@@ -0,0 +1,73 @@
+<?php
+
+namespace StatsDBundle\StatsDBundle\Services;
+
+class Config
+{
+    
+    const INI_FILE =  '../Resources/config/statsd.ini';
+
+    /**
+     * @var Config
+     */
+    private static $_instance;
+
+    /**
+     * @var array
+     */
+    private $_data;
+
+
+    private function __construct($ini_file = self::INI_FILE)
+    {
+        if ($ini_file == self::INI_FILE) {
+            $ini_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . $ini_file;
+        }
+        $this->_data = parse_ini_file($ini_file, true);
+    }
+
+    /**
+     * @return Config
+     */
+    public static function getInstance()
+    {
+        if (!self::$_instance) {
+            self::$_instance = new self();
+        }
+
+        return self::$_instance;
+    }
+
+    /**
+     * @param string $section
+     * 
+     * @return boolean
+     */
+    public function isEnabled($section)
+    {
+        return isset($this->_data[$section]);
+    }
+
+    /**
+     * @param string $name
+     * 
+     * @return string
+     */
+    public function getConfig($name)
+    {
+        $name_array = explode('.', $name, 2);
+
+        if (count($name_array) < 2) {
+            return;
+        }
+
+        list($section, $param) = $name_array;
+
+        if (!isset($this->_data[$section][$param])) {
+            return;
+        }
+
+        return $this->_data[$section][$param];
+    }
+
+}

+ 150 - 0
Services/StatsD.php

@@ -0,0 +1,150 @@
+<?php
+
+namespace StatsDBundle\StatsDBundle\Services;
+
+/**
+ * Sends statistics to the stats daemon over UDP
+ */
+class StatsD
+{
+
+    /**
+     * Sets one or more timing values
+     *
+     * @param string|array $stats The metric(s) to set.
+     * @param float $time The elapsed time (ms) to log
+     */
+    public static function timing($stats, $time)
+    {
+        StatsD::updateStats($stats, $time, 1, 'ms');
+    }
+
+    /**
+     * Sets one or more gauges to a value
+     *
+     * @param string|array $stats The metric(s) to set.
+     * @param float $value The value for the stats.
+     */
+    public static function gauge($stats, $value)
+    {
+        StatsD::updateStats($stats, $value, 1, 'g');
+    }
+
+    /**
+     * A "Set" is a count of unique events.
+     * This data type acts like a counter, but supports counting
+     * of unique occurences of values between flushes. The backend
+     * receives the number of unique events that happened since
+     * the last flush.
+     *
+     * The reference use case involved tracking the number of active
+     * and logged in users by sending the current userId of a user
+     * with each request with a key of "uniques" (or similar).
+     *
+     * @param string|array $stats The metric(s) to set.
+     * @param float $value The value for the stats.
+     */
+    public static function set($stats, $value)
+    {
+        StatsD::updateStats($stats, $value, 1, 's');
+    }
+
+    /**
+     * Increments one or more stats counters
+     *
+     * @param string|array $stats The metric(s) to increment.
+     * @param float|1 $sampleRate the rate (0-1) for sampling.
+     * 
+     * @return boolean
+     */
+    public static function increment($stats, $sampleRate = 1)
+    {
+        StatsD::updateStats($stats, 1, $sampleRate, 'c');
+    }
+
+    /**
+     * Decrements one or more stats counters.
+     *
+     * @param string|array $stats The metric(s) to decrement.
+     * @param float|1 $sampleRate the rate (0-1) for sampling.
+     * 
+     * @return boolean
+     */
+    public static function decrement($stats, $sampleRate = 1)
+    {
+        StatsD::updateStats($stats, -1, $sampleRate, 'c');
+    }
+
+    /**
+     * Updates one or more stats.
+     *
+     * @param string|array $stats The metric(s) to update. Should be either a string or array of metrics.
+     * @param int|1 $delta The amount to increment/decrement each metric by.
+     * @param float|1 $sampleRate the rate (0-1) for sampling.
+     * @param string|c $metric The metric type ("c" for count, "ms" for timing, "g" for gauge, "s" for set)
+     * 
+     * @return boolean
+     */
+    public static function updateStats($stats, $delta = 1, $sampleRate = 1, $metric = 'c')
+    {
+        if (!is_array($stats)) {
+            $stats = array($stats);
+        }
+        $data = array();
+        foreach ($stats as $stat) {
+            $data[$stat] = "$delta|$metric";
+        }
+
+        StatsD::send($data, $sampleRate);
+    }
+
+    /**
+     * Squirt the metrics over UDP
+     * 
+     * @param array $data
+     * @param int $sampleRate
+     * 
+     * @return void
+     */
+    public static function send($data, $sampleRate = 1)
+    {
+        $config = Config::getInstance();
+        if (!$config->isEnabled("statsd")) {
+            return;
+        }
+
+        // sampling
+        $sampledData = array();
+
+        if ($sampleRate < 1) {
+            foreach ($data as $stat => $value) {
+                if ((mt_rand() / mt_getrandmax()) <= $sampleRate) {
+                    $sampledData[$stat] = "$value|@$sampleRate";
+                }
+            }
+        } else {
+            $sampledData = $data;
+        }
+
+        if (empty($sampledData)) {
+            return;
+        }
+
+        // Wrap this in a try/catch - failures in any of this should be silently ignored
+        try {
+            $host = $config->getConfig("statsd.host");
+            $port = $config->getConfig("statsd.port");
+            $fp = fsockopen("udp://$host", $port, $errno, $errstr);
+            if (!$fp) {
+                return;
+            }
+            foreach ($sampledData as $stat => $value) {
+                fwrite($fp, "$stat:$value");
+            }
+            fclose($fp);
+        } catch (Exception $e) {
+            
+        }
+    }
+
+}