Created
August 12, 2014 22:16
-
-
Save bubba-h57/4d2d8860323a6993480a to your computer and use it in GitHub Desktop.
Simple class for logging directly to Splunk via API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use Guzzle\Http\Client; | |
/** | |
* Simple class for logging events directly to splunk, via their API | |
* in the generic_single_line format. | |
* @author Bubba | |
* | |
*/ | |
class SplunkLogger { | |
// Define some constant log levels, these look standard | |
const OFF = 2147483647; | |
const FATAL = 50000; | |
const ERROR = 40000; | |
const WARN = 30000; | |
const INFO = 20000; | |
const DEBUG = 10000; | |
const TRACE = 5000; | |
const ALL = -2147483647; | |
/** | |
* Splunk API Host Name | |
* @var string | |
*/ | |
public $apiHost = NULL; | |
/** | |
* Splunk Project ID | |
* @var string | |
*/ | |
public $projectID = NULL; | |
/** | |
* Splunk Project Access Token | |
* @var string | |
*/ | |
public $accessToken = NULL; | |
/** | |
* Splunk API Version to use | |
* @var number | |
*/ | |
public $apiVersion = 1; | |
/** | |
* Splunk API End Point | |
* @var string | |
*/ | |
public $apiEndpoint = 'inputs/http'; | |
/** | |
* Splunk API URL Scheme | |
* @var string | |
*/ | |
public $urlScheme = 'https'; | |
/** | |
* Splunk Source Type | |
* @var string | |
*/ | |
public $sourcetype = 'generic_single_line'; | |
/** | |
* Identify the application using the logger. | |
* @var string | |
*/ | |
public $app = 'unknown'; | |
/** | |
* Additional, static data to be added to the log payload | |
* @var string | |
*/ | |
public $addToPayload = NULL; | |
/** | |
* Fire and forget? Doesn't wait around to ensure that the | |
* event actually made it to splunk. Good for performance, | |
* bad on web servers. | |
* @var boolean | |
*/ | |
public $logAndForget = FALSE; | |
/** | |
* Our Guzzle Client | |
* @var Guzzle\Http\Client | |
*/ | |
private $client = NULL; | |
/** | |
* Our current acceptable Log Level | |
* @var number | |
*/ | |
private $level = NULL; | |
/** | |
* Map our log levels to strings | |
* @var array | |
*/ | |
public $levelMap = NULL; | |
/** | |
* Constructor! | |
* | |
* @param string $apiHost Find this in your splunk projects data settings for Rest API | |
* @param string $projectID Find this in your splunk projects data settings for Rest API | |
* @param string $accessToken Find this in your splunk projects data settings for Rest API | |
* @param string $app Provide a simple string denoting your application | |
* @param number $logLevel Pick a log level | |
* @param number $apiVersion What version of the Splunk API to Use | |
* @param string $apiEndpoint What endpoint of the Splunk API to use | |
* @param string $urlScheme What URL Scheme | |
*/ | |
public function SplunkLogger ($apiHost, $projectID, $accessToken, $app, $logLevel = self::ERROR, $apiVersion = 1, $apiEndpoint = 'inputs/http', $urlScheme = 'https'){ | |
$this->apiHost = $apiHost; | |
$this->projectID = $projectID; | |
$this->accessToken = $accessToken; | |
$this->apiVersion = $apiVersion; | |
$this->apiEndpoint = $apiEndpoint; | |
$this->urlScheme = $urlScheme; | |
$this->app = $app; | |
$this->initLevelMap(); | |
$this->enableFor($logLevel); | |
} | |
/** | |
* Initialized the Level Map for use. | |
*/ | |
private function initLevelMap(){ | |
$this->levelMap[self::OFF] = 'OFF'; | |
$this->levelMap[self::FATAL] = 'FATAL'; | |
$this->levelMap[self::ERROR] = 'ERROR'; | |
$this->levelMap[self::WARN] = 'WARN'; | |
$this->levelMap[self::INFO] = 'INFO'; | |
$this->levelMap[self::DEBUG] = 'DEBUG'; | |
$this->levelMap[self::TRACE] = 'TRACE'; | |
$this->levelMap[self::ALL] = 'ALL'; | |
} | |
/** | |
* Looks up a string representation of the log level | |
* @param string|number $level | |
* @return string | |
*/ | |
private function mapLevel($level){ | |
return $this->levelMap[$level]; | |
} | |
/** | |
* For now, we only support a single source type, but this | |
* fetches it for you! | |
* @return string | |
*/ | |
public function getSourceType(){ | |
return $this->sourcetype; | |
} | |
/** | |
* Take all those variables, and make me a nice URL to use | |
* @return string | |
*/ | |
public function getUrl(){ | |
return $this->urlScheme . '://'. $this->apiHost . '/' . $this->apiVersion . '/' . $this->apiEndpoint . | |
'?index=' . $this->projectID . '&sourcetype=' . $this->sourcetype; | |
} | |
/** | |
* At some point, we are going to want an HTTP Client | |
* @return \Guzzle\Http\Client | |
*/ | |
private function getClient(){ | |
if (is_null($this->client)){ | |
$this->client = new Guzzle\Http\Client(); | |
} | |
return $this->client; | |
} | |
/** | |
* Send a debug mesage | |
* @param string $message | |
* @return boolean | |
*/ | |
public function debug($message){ | |
return $this->logger(self::DEBUG, $message); | |
} | |
/** | |
* Send an info message | |
* @param string $message | |
* @return boolean | |
*/ | |
public function info($message){ | |
return $this->logger(self::INFO, $message); | |
} | |
/** | |
* Send an error message | |
* @param string $message | |
* @return boolean | |
*/ | |
public function error($message){ | |
return $this->logger(self::ERROR, $message); | |
} | |
/** | |
* Helper for sending error messages | |
* @param string $message | |
* @return boolean | |
*/ | |
public function err($message){ | |
return $this->error($message); | |
} | |
/** | |
* Send a fatal message | |
* @param string $message | |
* @return boolean | |
*/ | |
public function fatal($message){ | |
return $this->logger(self::FATAL, $message); | |
} | |
/** | |
* Send a warning message | |
* @param string $message | |
* @return boolean | |
*/ | |
public function warn($message){ | |
return $this->logger(self::WARN, $message); | |
} | |
/** | |
* Send a trace message | |
* @param string $message | |
* @return boolean | |
*/ | |
public function trace($message){ | |
return $this->logger(self::TRACE, $message); | |
} | |
/** | |
* Get a nice timestamp, with microseconds | |
* @return string | |
*/ | |
public function getTimeStamp(){ | |
return date_create_from_format('U.u', sprintf('%.f', microtime(true)))->format("D M j G:i:s.u T Y"); | |
} | |
/** | |
* Actually sends a log | |
* @param number $level | |
* @param string $message | |
* @return boolean | |
*/ | |
private function logger($level, $message) { | |
// If our logger is not enalbed for this, just shortcut and return | |
if (! $this->isEnabledFor($level)){ | |
exit("not enalbed"); | |
return true; | |
} | |
// Generate the event message | |
$payload = sprintf('%s %s app="%s" level="%s" message="%s"', $this->getTimeStamp(), $this->addToPayload, $this->app, $this->mapLevel($level), $message); | |
$payload = trim(preg_replace('/\s+/', ' ', $payload)); | |
// IF we fire and forget | |
if ($this->logAndForget){ | |
$pid = pcntl_fork(); | |
switch($pid) { | |
case -1: | |
print "Could not fork!\n"; | |
exit; | |
case 0: | |
$this->getClient()->post($this->getUrl())->setAuth('x',$this->accessToken )->setBody($payload)->send(); | |
exit; | |
default: | |
return true; | |
} | |
// Otherwise, send and wait | |
}else{ | |
try { | |
$response = $this->getClient()->post($this->getUrl())->setAuth('x',$this->accessToken )->setBody($payload)->send(); | |
return true; | |
} catch (Exception $e) { | |
return false; | |
} | |
} | |
} | |
/** | |
* Sets the log level for this logger | |
* @param number $level | |
*/ | |
public function enableFor($level){ | |
$this->level = $this->toLevel($level); | |
} | |
/** | |
* Converts a number or string to the proper log level | |
* @param string|number $level | |
* @return string | |
*/ | |
public static function toLevel($level){ | |
if(is_int($level)) { | |
switch($level) { | |
case self::ALL: | |
return self::ALL; | |
case self::TRACE: | |
return self::TRACE; | |
case self::DEBUG: | |
return self::DEBUG; | |
case self::INFO: | |
return self::INFO; | |
case self::WARN: | |
return self::WARN; | |
case self::ERROR: | |
return self::ERROR; | |
case self::FATAL: | |
return self::FATAL; | |
case self::OFF: | |
return self::OFF; | |
default: | |
return self::ERROR; | |
} | |
} else { | |
switch(strtoupper($arg)) { | |
case 'ALL': | |
return self::ALL; | |
case 'TRACE': | |
return self::TRACE; | |
case 'DEBUG': | |
return self::DEBUG; | |
case 'INFO': | |
return self::INFO; | |
case 'WARN': | |
return self::WARN; | |
case 'ERROR': | |
return self::ERROR; | |
case 'FATAL': | |
return self::FATAL; | |
case 'OFF': | |
return self::OFF; | |
default: | |
return self::ERROR; | |
} | |
} | |
} | |
/** | |
* Checks a message level against the loggers level | |
* to determine if the logger is enabled for the message | |
* @param number $level | |
* @return boolean | |
*/ | |
public function isEnabledFor($level){ | |
return ($level >= $this->level); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment