Skip to content

Instantly share code, notes, and snippets.

@bubba-h57
Created August 12, 2014 22:16
Show Gist options
  • Save bubba-h57/4d2d8860323a6993480a to your computer and use it in GitHub Desktop.
Save bubba-h57/4d2d8860323a6993480a to your computer and use it in GitHub Desktop.
Simple class for logging directly to Splunk via API
<?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