迁移原来 LMR 开发的日志查询

mobile-first
LiaoYijun 6 years ago
parent 51e90ac73b
commit 59aa831764

11
splunk/.gitignore vendored

@ -0,0 +1,11 @@
# OS X
.DS_Store
# Generated documentation
apidocs/
# Generated code coverage report
coverage/
# Local settings
*.local.php

@ -0,0 +1,33 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Intercepts failed attempts to load classes of the form 'Splunk_*'
* and automatically includes the appropriate PHP file for the class.
*/
function Splunk_autoload($className)
{
if (substr_compare($className, 'Splunk_', 0, strlen('Splunk_')) != 0)
return false;
$file = str_replace('_', '/', $className);
return include dirname(__FILE__) . "/$file.php";
}
spl_autoload_register('Splunk_autoload');
if (version_compare(PHP_VERSION, '5.2.11') < 0)
die('The Splunk SDK for PHP requires PHP 5.2.11 or later.');

@ -0,0 +1,33 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Thrown when an attempt is made to access a Splunk entity with a name
* that matches multiple entities in the current (non-exact) namespace.
*
* @package Splunk
*/
class Splunk_AmbiguousEntityNameException extends RuntimeException
{
/** @internal */
public function __construct($name)
{
parent::__construct(
"Multiple entities exist with name '{$name}'. " .
"Specify an exact namespace to disambiguate.");
}
}

@ -0,0 +1,90 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Contains utilities for parsing Atom feeds received from the Splunk REST API.
*
* @package Splunk
* @internal
*/
class Splunk_AtomFeed
{
/** Name of the 's' namespace in Splunk Atom feeds. */
const NS_S = 'http://dev.splunk.com/ns/rest';
/**
* Parses and returns the value inside the specified XML element.
*
* @param SimpleXMLElement $containerXml
* @return mixed
*/
public static function parseValueInside($containerXml)
{
$dictValue = $containerXml->children(Splunk_AtomFeed::NS_S)->dict;
$listValue = $containerXml->children(Splunk_AtomFeed::NS_S)->list;
if (Splunk_XmlUtil::elementExists($dictValue))
{
return Splunk_AtomFeed::parseDict($dictValue);
}
else if (Splunk_XmlUtil::elementExists($listValue))
{
return Splunk_AtomFeed::parseList($listValue);
}
else // value is scalar
{
return Splunk_XmlUtil::getTextContent($containerXml);
}
}
/*
* Example of $dictXml:
*
* <s:dict>
* <s:key name="k1">v1</s:key>
* <s:key name="k2">v2</s:key>
* </s:dict>
*/
private static function parseDict($dictXml)
{
$dict = array();
foreach ($dictXml->children(Splunk_AtomFeed::NS_S)->key as $keyXml)
{
$key = Splunk_XmlUtil::getAttributeValue($keyXml, 'name');
$value = Splunk_AtomFeed::parseValueInside($keyXml);
$dict[$key] = $value;
}
return $dict;
}
/*
* Example of $listXml:
*
* <s:list>
* <s:item>e1</s:item>
* <s:item>e2</s:item>
* </s:list>
*/
private static function parseList($listXml)
{
$list = array();
foreach ($listXml->children(Splunk_AtomFeed::NS_S)->item as $itemXml)
$list[] = Splunk_AtomFeed::parseValueInside($itemXml);
return $list;
}
}

@ -0,0 +1,313 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Represents a collection of entities accessible through Splunk's REST API.
*
* @package Splunk
*/
class Splunk_Collection extends Splunk_Endpoint
{
private $entitySubclass;
/**
* @internal
*
* @param Splunk_Service $service
* @param string $path
* @param string $entitySubclass (optional) name of the entity subclass
* that this collection's children will
* be instantiated with.
*/
public function __construct($service, $path, $entitySubclass='Splunk_Entity')
{
parent::__construct($service, $path);
$this->entitySubclass = $entitySubclass;
}
// === Accessors ===
/**
* Not implemented.
*/
protected function getSearchNamespace()
{
// (The namespace cannot presently be overridden on a per-collection basis.)
return NULL;
}
// === Operations ===
// NOTE: This method isn't called 'list' only because PHP treats 'list' as a
// pseudo-keyword and gets confused when it's used as a method name.
/**
* Lists this collection's entities, returning a list of loaded entities.
*
* By default, all items in the collection are returned. For large
* collections, it is advisable to fetch items using multiple calls with
* the paging options (i.e. 'offset' and 'count').
*
* @param array $args (optional) {<br/>
* **namespace**: (optional) {Splunk_Namespace} The namespace in which
* to list entities. Defaults to the service's namespace.<br/>
*
* **count**: (optional) The maximum number of items to return,
* or -1 to return as many as possible.
* Defaults to returning as many as possible.<br/>
* **offset**: (optional) The offset of the first item to return.
* Defaults to 0.<br/>
*
* **search**: (optional) The search expression to filter responses
* with. For example, "foo" matches any object that has
* "foo" in a substring of a field. Similarly the
* expression "field_name=field_value" matches only objects
* that have a "field_name" field with the value
* "field_value".<br/>
* **sort_dir**: (optional) The direction to sort returned items.<br/>
* Valid values:<br/>
* - "asc": Sort in ascending order.<br/>
* - "desc": Sort in descending order.<br/>
* Defaults to "asc".<br/>
* **sort_key**: (optional) The field to use for sorting.
* Defaults to "name".<br/>
* **sort_mode**: (optional) The sorting algorithm to use. Valid values:<br/>
* - "auto": If all values of the field are numbers,
* sort numerically. Otherwise, sort
* alphabetically.<br/>
* - "alpha": Sort alphabetically.<br/>
* - "alpha_case": Sort alphabetically, case-sensitive.<br/>
* - "num": Sort numerically.<br/>
* Defaults to "auto".<br/>
* }
* @return array the entities in the listing.
* @throws Splunk_IOException
*/
public function items($args=array())
{
$args = array_merge(array(
'count' => -1,
), $args);
if ($args['count'] <= 0 && $args['count'] != -1)
throw new InvalidArgumentException(
'Count must be positive or -1 (infinity).');
if ($args['count'] == -1)
$args['count'] = 0; // infinity value for the REST API
$response = $this->sendGet('', $args);
return $this->loadEntitiesFromResponse($response);
}
/**
* Returns an array of entities from the given response.
*
* @param $response
* @return array array of Splunk_Entry.
*/
private function loadEntitiesFromResponse($response)
{
$xml = new SimpleXMLElement($response->body);
$entities = array();
foreach ($xml->entry as $entry)
{
$entities[] = $this->loadEntityFromEntry($entry);
}
return $entities;
}
/**
* Returns an entity from the given entry element.
*
* @param SimpleXMLElement $entry an <entry> element.
* @return Splunk_Entry
*/
private function loadEntityFromEntry($entry)
{
return new $this->entitySubclass(
$this->service,
$this->getEntityPath($entry->title),
$entry);
}
/**
* Returns the unique entity with the specified name in this collection.
*
* @param string $name The name of the entity to search for.
* @param Splunk_Namespace|NULL $namespace
* (optional) {Splunk_Namespace} The namespace in which
* to search. Defaults to the service's namespace.
* @return Splunk_Entity
* @throws Splunk_NoSuchEntityException
* If no such entity exists.
* @throws Splunk_AmbiguousEntityNameException
* If multiple entities with the specified name
* exist in the specified namespace.
* @throws Splunk_IOException
*/
public function get($name, $namespace=NULL)
{
$this->checkName($name);
$this->checkNamespace($namespace);
try
{
$response = $this->sendGet($this->getEntityRelativePath($name), array(
'namespace' => $namespace,
'count' => 0,
));
$entities = $this->loadEntitiesFromResponse($response);
}
catch (Splunk_HttpException $e)
{
if ($e->getResponse()->status == 404)
$entities = array();
else
throw $e;
}
if (count($entities) == 0)
{
throw new Splunk_NoSuchEntityException($name);
}
else if (count($entities) == 1)
{
return $entities[0];
}
else
{
throw new Splunk_AmbiguousEntityNameException($name);
}
}
/**
* Returns a reference to the unique entity with the specified name in this
* collection. Loading of the entity is deferred until its first use.
*
* @param string $name The name of the entity to search for.
* @param Splunk_Namespace|NULL $namespace
* (optional) {Splunk_Namespace} The namespace in which
* to search. Defaults to the service's namespace.
* @return Splunk_Entity
*/
public function getReference($name, $namespace=NULL)
{
$this->checkName($name);
$this->checkNamespace($namespace);
return new $this->entitySubclass(
$this->service,
$this->getEntityPath($name),
NULL,
$namespace);
}
/**
* Creates a new entity in this collection.
*
* @param string $name The name of the entity to create.
* @param array $args (optional) Entity-specific creation arguments,
* merged with {<br/>
* **namespace**: (optional) {Splunk_Namespace} The namespace in which
* to create the entity. Defaults to the service's
* namespace.<br/>
* }
* @return Splunk_Entity
* @throws Splunk_IOException
*/
public function create($name, $args=array())
{
$this->checkName($name);
$args = array_merge(array(
'name' => $name,
), $args);
$response = $this->sendPost('', $args);
if ($response->body === '')
{
// This endpoint doesn't return the content of the new entity.
return $this->getReference($name);
}
else
{
$xml = new SimpleXMLElement($response->body);
return $this->loadEntityFromEntry($xml->entry);
}
}
/**
* Deletes an entity from this collection.
*
* @param string $name The name of the entity to delete.
* @param array $args (optional) Entity-specific deletion arguments,
* merged with {<br/>
* **namespace**: (optional) {Splunk_Namespace} The namespace in which
* to find the entity. Defaults to the service's
* namespace.<br/>
* }
* @throws Splunk_IOException
*/
public function delete($name, $args=array())
{
$this->checkName($name);
$this->sendDelete($this->getEntityRelativePath($name), $args);
}
// === Utility ===
/**
* Returns the absolute path of the child entity with the specified name.
*/
private function getEntityPath($name)
{
return $this->path . $this->getEntityRelativePath($name);
}
/**
* Returns the relative path of the child entity with the specified name.
*/
private function getEntityRelativePath($name)
{
return urlencode($name);
}
/**
* Ensures that the specified name is not NULL or empty.
*/
protected function checkName($name)
{
if ($name === NULL || $name === '')
throw new InvalidArgumentException('Invalid empty name.');
}
/**
* Ensures that the specified namespace is a Splunk_Namespace if it is
* not NULL.
*/
protected function checkNamespace($namespace)
{
// (It's not uncommon to attempt to pass an args dictionary after
// the $name argument, so perform an explicit type check to make sure
// the caller isn't trying to do this.)
if ($namespace !== NULL && !($namespace instanceof Splunk_Namespace))
throw new InvalidArgumentException(
'Namespace must be NULL or a Splunk_Namespace.');
}
}

@ -0,0 +1,30 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Thrown when unable to connect to a Splunk server.
*
* @package Splunk
*/
class Splunk_ConnectException extends Splunk_IOException
{
/** @internal */
public function __construct($message=NULL, $code=0)
{
parent::__construct($message, $code);
}
}

@ -0,0 +1,302 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Enables clients to issue HTTP requests to a Splunk server.
*
* @package Splunk
*/
class Splunk_Context
{
private $username;
private $password;
private $token;
private $host;
private $port;
private $scheme;
private $namespace;
private $http;
/**
* Constructs a new context with the specified parameters.
*
* @param array $args {<br/>
* **username**: (optional) The username to login with. Defaults to
* "admin".<br/>
* **password**: (optional) The password to login with. Defaults to
* "changeme".<br/>
* **token**: (optional) The authentication token to use. If provided,
* the username and password are ignored and there is no
* need to call login(). In the format "Splunk SESSION_KEY".
* <br/>
* **host**: (optional) The hostname of the Splunk server. Defaults to
* "localhost".<br/>
* **port**: (optional) The port of the Splunk server. Defaults to
* 8089.<br/>
* **scheme**: (optional) The scheme to use: either "http" or "https".
* Defaults to "https".<br/>
* **namespace**: (optional) Namespace that all object lookups will
* occur in by default. Defaults to
* `Splunk_Namespace::createDefault()`.<br/>
* **http**: (optional) An Http object that will be used for
* performing HTTP requests. This is intended for testing
* only.<br/>
* }
*/
public function __construct($args=array())
{
$args = array_merge(array(
'username' => 'admin',
'password' => 'changeme',
'token' => NULL,
'host' => 'localhost',
'port' => 8089,
'scheme' => 'https',
'namespace' => Splunk_Namespace::createDefault(),
'http' => new Splunk_Http(),
), $args);
$this->username = $args['username'];
$this->password = $args['password'];
$this->token = $args['token'];
$this->host = $args['host'];
$this->port = $args['port'];
$this->scheme = $args['scheme'];
$this->namespace = $args['namespace'];
$this->http = $args['http'];
}
// === Operations ===
/**
* Authenticates to the Splunk server.
*/
public function login()
{
$response = $this->http->post($this->url('/services/auth/login'), array(
'username' => $this->username,
'password' => $this->password,
));
$sessionKey = Splunk_XmlUtil::getTextContentAtXpath(
new SimpleXMLElement($response->body),
'/response/sessionKey');
$this->token = "Splunk {$sessionKey}";
}
// === HTTP ===
/**
* Sends an HTTP GET request to the endpoint at the specified path.
*
* @param string $path relative or absolute URL path.
* @param array $args (optional) query parameters, merged with {<br/>
* **namespace**: (optional) namespace to use, or NULL to use
* this context's default namespace.<br/>
* }
* @return Splunk_HttpResponse
* @throws Splunk_IOException
* @see Splunk_Http::get()
*/
public function sendGet($path, $args=array())
{
return $this->sendSimpleRequest('get', $path, $args);
}
/**
* Sends an HTTP POST request to the endpoint at the specified path.
*
* @param string $path relative or absolute URL path.
* @param array $args (optional) form parameters to send in the
* request body, merged with {<br/>
* **namespace**: (optional) namespace to use, or NULL to use
* this context's default namespace.<br/>
* }
* @return Splunk_HttpResponse
* @throws Splunk_IOException
* @see Splunk_Http::post()
*/
public function sendPost($path, $args=array())
{
return $this->sendSimpleRequest('post', $path, $args);
}
/**
* Sends an HTTP DELETE request to the endpoint at the specified path.
*
* @param string $path relative or absolute URL path.
* @param array $args (optional) query parameters, merged with {<br/>
* **namespace**: (optional) namespace to use, or NULL to use
* this context's default namespace.<br/>
* }
* @return Splunk_HttpResponse
* @throws Splunk_IOException
* @see Splunk_Http::delete()
*/
public function sendDelete($path, $args=array())
{
return $this->sendSimpleRequest('delete', $path, $args);
}
/**
* Sends a simple HTTP request to the endpoint at the specified path.
*/
private function sendSimpleRequest($method, $path, $args)
{
list($params, $namespace) =
Splunk_Util::extractArgument($args, 'namespace', NULL);
return $this->http->$method(
$this->url($path, $namespace),
$params,
$this->getRequestHeaders());
}
/**
* Sends an HTTP request to the endpoint at the specified path.
*
* @param string $method the HTTP method (ex: 'GET' or 'POST').
* @param string $path relative or absolute URL path.
* @param array $requestHeaders (optional) dictionary of header names and
* values.
* @param string $requestBody (optional) content to send in the request.
* @param array $args (optional) query parameters, merged with
* {<br/>
* **namespace**: (optional) namespace to use, or NULL to use
* this context's default namespace.<br/>
* }
* @return Splunk_HttpResponse
* @throws Splunk_IOException
* @see Splunk_Http::request()
*/
public function sendRequest(
$method, $path, $requestHeaders=array(), $requestBody='', $args=array())
{
list($params, $namespace) =
Splunk_Util::extractArgument($args, 'namespace', NULL);
$url = $this->url($path, $namespace);
$fullUrl = (count($params) == 0)
? $url
: $url . '?' . http_build_query($params);
$requestHeaders = array_merge(
$this->getRequestHeaders(),
$requestHeaders);
return $this->http->request(
$method,
$fullUrl,
$requestHeaders,
$requestBody);
}
/** Returns the standard headers to send on each HTTP request. */
private function getRequestHeaders()
{
return array(
'Authorization' => $this->token,
);
}
// === Accessors ===
/**
* Gets the default namespace for collection and entity operations.
*
* @return Splunk_Namespace The default namespace that will be used
* to perform collection and entity operations
* when none is explicitly specified.
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* Gets the token used to authenticate HTTP requests after logging in.
*
* @return string The token used to authenticate HTTP requests
* after logging in.
*/
public function getToken()
{
return $this->token;
}
/**
* Gets the hostname of the Splunk server.
*
* @return string The hostname of the Splunk server.
*/
public function getHost()
{
return $this->host;
}
/**
* Gets the port of the Splunk server.
*
* @return string The port of the Splunk server.
*/
public function getPort()
{
return $this->port;
}
/**
* Gets the scheme to use.
*
* @return string The scheme to use: either "http" or "https".
*/
public function getScheme()
{
return $this->scheme;
}
// === Utility ===
/**
* Returns the absolute URL.
*
* @param string $path Relative or absolute URL path.
* @param Splunk_Namespace|NULL $namespace
* @return string Absolute URL.
*/
private function url($path, $namespace=NULL)
{
return "{$this->scheme}://{$this->host}:{$this->port}{$this->abspath($path, $namespace)}";
}
/**
* Returns the absolute URL path.
*
* @param string $path Relative or absolute URL path.
* @param Splunk_Namespace|NULL $namespace
* @return string Absolute URL path.
*/
private function abspath($path, $namespace=NULL)
{
if ((strlen($path) >= 1) && ($path[0] == '/'))
return $path;
if ($namespace === NULL)
$namespace = $this->namespace;
return $namespace->getPathPrefix() . $path;
}
}

@ -0,0 +1,111 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Represents a single endpoint in the Splunk REST API.
*
* @package Splunk
*/
abstract class Splunk_Endpoint
{
protected $service;
protected $path;
/** @internal */
public function __construct($service, $path)
{
$this->service = $service;
$this->path = $path;
}
// === Accessors ===
/**
* Returns the namespace in which this endpoint resides, or NULL to use
* the context's default namespace.
*
* @return Splunk_Namespace|NULL The namespace in which this endpoint
* resides, or NULL to use the context's
* default namespace.
* Possibly a non-exact namespace.
*/
protected abstract function getSearchNamespace();
// === HTTP ===
/**
* Sends an HTTP GET request relative to this endpoint.
*
* @param string $relativePath relative URL path.
* @param array $args (optional) query parameters, merged with {<br/>
* **namespace**: (optional) namespace to use, or NULL to use
* the context's default namespace.<br/>
* }
* @return Splunk_HttpResponse
* @throws Splunk_IOException
* @see Splunk_Http::get()
*/
public function sendGet($relativePath, $args=array())
{
return $this->sendSimpleRequest('sendGet', $relativePath, $args);
}
/**
* Sends an HTTP POST request relative to this endpoint.
*
* @param string $relativePath relative URL path.
* @param array $args (optional) form parameters to send in the request
* body, merged with {<br/>
* **namespace**: (optional) namespace to use, or NULL to use
* the context's default namespace.<br/>
* }
* @return Splunk_HttpResponse
* @throws Splunk_IOException
* @see Splunk_Http::post()
*/
public function sendPost($relativePath, $args=array())
{
return $this->sendSimpleRequest('sendPost', $relativePath, $args);
}
/**
* Sends an HTTP DELETE request relative to this endpoint.
*
* @param string $relativePath relative URL path.
* @param array $args (optional) query parameters, merged with {<br/>
* **namespace**: (optional) namespace to use, or NULL to use
* the context's default namespace.<br/>
* }
* @return Splunk_HttpResponse
* @throws Splunk_IOException
* @see Splunk_Http::delete()
*/
public function sendDelete($relativePath, $args=array())
{
return $this->sendSimpleRequest('sendDelete', $relativePath, $args);
}
/** Sends a simple request relative to this endpoint. */
private function sendSimpleRequest($method, $relativePath, $args=array())
{
$args = array_merge(array(
'namespace' => $this->getSearchNamespace(),
), $args);
return $this->service->$method("{$this->path}{$relativePath}", $args);
}
}

@ -0,0 +1,298 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Represents an entity accessible through Splunk's REST API.
*
* @package Splunk
*/
class Splunk_Entity extends Splunk_Endpoint implements ArrayAccess
{
private $loaded = FALSE;
private $entry;
private $namespace;
private $content;
/**
* @internal
*
* @param Splunk_Service $service
* @param string $path
* @param SimpleXMLElement|NULL $entry
* (optional) The <entry> for this
* entity, as received from the REST API.
* If omitted, will be loaded on demand.
* @param Splunk_Namespace|NULL $namespace
* (optional) The namespace from which to
* load this entity, or NULL to use the
* $service object's default namespace.
* Does not apply if this entity is
* already loaded (i.e. if $entry is not
* NULL).
*/
public function __construct($service, $path, $entry=NULL, $namespace=NULL)
{
parent::__construct($service, $path);
$this->entry = $entry;
if ($this->entry != NULL)
{
if ($namespace !== NULL)
throw new InvalidArgumentException(
'Cannot specify an entry and a namespace simultaneously. ' .
'The namespace will be inferred from the entry.');
$this->parseContentsFromEntry();
$this->namespace = $this->getNamespace(); // extract from 'eai:acl'
}
else
{
$this->namespace = $namespace;
}
}
// === Load ===
/**
* Loads this resource if not already done. Returns self.
*
* @return Splunk_Entity This entity.
* @throws Splunk_IOException
*/
protected function validate($fetchArgs=array())
{
if (!$this->loaded)
{
$this->load($fetchArgs);
assert($this->loaded);
}
return $this;
}
/**
* Loads this resource.
*
* @throws Splunk_IOException
*/
private function load($fetchArgs)
{
$response = $this->fetch($fetchArgs);
$xml = new SimpleXMLElement($response->body);
$this->entry = $this->extractEntryFromRootXmlElement($xml);
$this->parseContentsFromEntry();
}
/**
* Fetches this entity's Atom feed from the Splunk server.
*
* @throws Splunk_IOException
*/
protected function fetch($fetchArgs)
{
return $this->sendGet('');
}
/** Returns the <entry> element inside the root element. */
protected function extractEntryFromRootXmlElement($xml)
{
if (!Splunk_XmlUtil::isSingleElement($xml->entry))
{
// Extract name from path since we can't extract it from the
// entity content here.
$pathComponents = explode('/', $this->path);
$name = $pathComponents[count($pathComponents) - 1];
throw new Splunk_AmbiguousEntityNameException($name);
}
return $xml->entry;
}
/** Parses the entry's contents. */
private function parseContentsFromEntry()
{
$this->content = Splunk_AtomFeed::parseValueInside($this->entry->content);
$this->loaded = TRUE;
}
/** Returns a value that indicates whether the entity has been loaded. */
protected function isLoaded()
{
return $this->loaded;
}
/**
* Refreshes this entity's properties from the Splunk server.
*
* @return Splunk_Entity This entity.
* @throws Splunk_IOException
*/
public function refresh()
{
if ($this->loaded)
{
// Remember this entity's exact namespace, so that a reload
// will occur in the correct namespace.
$this->namespace = $this->getNamespace();
}
$this->loaded = FALSE;
return $this->validate();
}
// === Accessors ===
/**
* Gets an array that contains the properties of this entity.
*
* @return array The properties of this entity.
*/
public function getContent()
{
return $this->validate()->content;
}
/**
* Gets the name of this entity.
*
* @return string The name of this entity.
* This name can be used to lookup this entity
* from its collection.
*/
public function getName()
{
return $this->getTitle();
}
/**
* Gets the title of this entity in the REST API.
*
* @return string The title of this entity in the REST API.
*/
protected function getTitle()
{
return (string) $this->validate()->entry->title;
}
/** Gets the namespace in which this entity resides. */
protected function getSearchNamespace()
{
return $this->namespace;
}
/**
* Gets the non-wildcarded namespace in which this entity resides.
*
* @return Splunk_Namespace The non-wildcarded namespace in which this
* entity resides.
*/
public function getNamespace()
{
// If this is an entity reference with an exact namespace, return it.
if (!$this->loaded)
{
$effectiveNamespace = $this->namespace;
if ($effectiveNamespace === NULL)
$effectiveNamespace = $this->service->getNamespace();
if ($effectiveNamespace->isExact())
return $effectiveNamespace;
}
// Extract the namespace from this entity's content.
$acl = $this['eai:acl'];
return Splunk_Namespace::createExact(
$acl['owner'], $acl['app'], $acl['sharing']);
}
// === ArrayAccess Methods ===
/**
* Gets the value of the specified entity property.
*
* @param string $key The name of an entity property.
* @return string The value of the specified entity property.
*/
public function offsetGet($key)
{
return $this->validate()->content[$key];
}
/** @internal */
public function offsetSet($key, $value)
{
throw new Splunk_UnsupportedOperationException();
}
/** @internal */
public function offsetUnset($key)
{
throw new Splunk_UnsupportedOperationException();
}
/**
* Gets a value that indicates whether the specified entity property exists.
*
* @param string $key The name of an entity property.
* @return string Whether the specified entity property exists.
*/
public function offsetExists($key)
{
return isset($this->validate()->content[$key]);
}
// === Operations ===
/**
* Deletes this entity.
*
* @throws Splunk_IOException
*/
public function delete()
{
$this->sendDelete('');
}
/**
* Updates this entity's properties.
*
* Note that the "name" property cannot be updated.
*
* @param array $args Dictionary of properties that will be changed,
* along with their new values.
* @return Splunk_Entity This entity.
* @throws Splunk_IOException
*/
public function update($args)
{
if (array_key_exists('name', $args))
throw new InvalidArgumentException(
'Cannot update the name of an entity.');
if (array_key_exists('namespace', $args))
throw new InvalidArgumentException(
'Cannot override the entity\'s namespace.');
// Update entity on server
$this->sendPost('', $args);
// Update cached content of entity
if ($this->loaded)
$this->content = array_merge($this->content, $args);
return $this;
}
}

@ -0,0 +1,232 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* HTTP abstraction layer.
*
* @package Splunk
* @internal
*/
class Splunk_Http
{
/**
* @param array $params (optional) query parameters.
* @see request()
*/
public function get($url, $params=array(), $requestHeaders=array())
{
return $this->requestWithParams('get', $url, $params, $requestHeaders);
}
/**
* @param array $params (optional) form parameters to send in the request body.
* @see request()
*/
public function post($url, $params=array(), $requestHeaders=array())
{
$requestHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
return $this->request(
'post', $url, $requestHeaders, http_build_query($params));
}
/**
* @param array $params (optional) query parameters.
* @see request()
*/
public function delete($url, $params=array(), $requestHeaders=array())
{
return $this->requestWithParams('delete', $url, $params, $requestHeaders);
}
private function requestWithParams(
$method, $url, $params, $requestHeaders)
{
$fullUrl = ($params === NULL || count($params) == 0)
? $url
: $url . '?' . http_build_query($params);
return $this->request($method, $fullUrl, $requestHeaders);
}
/**
* Sends an HTTP request and returns the response.
*
* @param string $method HTTP request method (ex: 'get').
* @param string $url URL to fetch.
* @param array $requestHeaders (optional) dictionary of header names and values.
* @param string $requestBody (optional) content to send in the request.
* @return Splunk_HttpResponse
* @throws Splunk_IOException
*/
public function request(
$method, $url, $requestHeaders=array(), $requestBody='')
{
$isHttp = (substr($url, 0, strlen('http:')) === 'http:');
$isHttps = (substr($url, 0, strlen('https:')) === 'https:');
if (!$isHttp && !$isHttps)
{
throw new InvalidArgumentException(
'URL scheme must be either HTTP or HTTPS.');
}
// The HTTP stream wrapper in PHP < 5.3.7 has a bug which
// injects junk at the end of HTTP requests, which breaks
// SSL connections. Fallback to cURL-based requests.
if ($isHttps && (version_compare(PHP_VERSION, '5.3.7') < 0))
return $this->requestWithCurl(
$method, $url, $requestHeaders, $requestBody);
$requestHeaderLines = array();
foreach ($requestHeaders as $k => $v)
$requestHeaderLines[] = "{$k}: {$v}";
$fopenContext = stream_context_create(array(
'http' => array(
'method' => strtoupper($method),
'header' => $requestHeaderLines,
'content' => $requestBody,
'follow_location' => 0, // don't follow HTTP 3xx automatically
'max_redirects' => 0, // [PHP 5.2] don't follow HTTP 3xx automatically
'ignore_errors' => TRUE, // don't throw exceptions on bad status codes
),
// LMR FIX!!!!!!
'ssl' => array(
'verify_peer' => false,
'allow_self_signed' => true,
'verify_peer_name' => false,
),
));
// NOTE: PHP does not perform certificate validation for HTTPS URLs.
// NOTE: fopen() magically sets the $http_response_header local variable.
$bodyStream = @fopen($url, 'rb', /*use_include_path=*/FALSE, $fopenContext);
if ($bodyStream === FALSE)
{
$errorInfo = error_get_last();
$errmsg = $errorInfo['message'];
$errno = $errorInfo['type'];
throw new Splunk_ConnectException($errmsg, $errno);
}
$headers = array();
$headerLines = $http_response_header;
$statusLine = array_shift($headerLines);
foreach ($headerLines as $line)
{
list($key, $value) = explode(':', $line, 2);
$headers[$key] = trim($value);
}
$statusLineComponents = explode(' ', $statusLine, 3);
$httpVersion = $statusLineComponents[0];
$status = intval($statusLineComponents[1]);
$reason = (count($statusLineComponents) == 3)
? $statusLineComponents[2]
: '';
$response = new Splunk_HttpResponse(array(
'status' => $status,
'reason' => $reason,
'headers' => $headers,
'bodyStream' => $bodyStream,
));
if ($status >= 400)
throw new Splunk_HttpException($response);
else
return $response;
}
private function requestWithCurl(
$method, $url, $requestHeaders=array(), $requestBody='')
{
$opts = array(
CURLOPT_URL => $url,
CURLOPT_TIMEOUT => 60, // secs
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HEADER => TRUE,
// disable SSL certificate validation
CURLOPT_SSL_VERIFYPEER => FALSE,
// LMR FIX !!!!!!!
CURLOPT_SSL_VERIFYHOST => FALSE,
);
foreach ($requestHeaders as $k => $v)
$opts[CURLOPT_HTTPHEADER][] = "$k: $v";
switch ($method)
{
case 'get':
$opts[CURLOPT_HTTPGET] = TRUE;
break;
case 'post':
$opts[CURLOPT_POST] = TRUE;
$opts[CURLOPT_POSTFIELDS] = $requestBody;
break;
default:
$opts[CURLOPT_CUSTOMREQUEST] = strtoupper($method);
break;
}
if (!($curl = curl_init()))
throw new Splunk_ConnectException('Unable to initialize cURL.');
if (!(curl_setopt_array($curl, $opts)))
throw new Splunk_ConnectException(curl_error($curl));
// NOTE: The entire HTTP response is read into memory here,
// which could be very large. Unfortunately the cURL
// interface does not provide a streaming alternative.
// To avoid this problem, use PHP 5.3.7+, which doesn't
// need cURL to perform HTTP requests.
if (!($response = curl_exec($curl)))
throw new Splunk_ConnectException(curl_error($curl));
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
$headerText = substr($response, 0, $headerSize);
$body = (strlen($response) == $headerSize)
? ''
: substr($response, $headerSize);
$headers = array();
$headerLines = explode("\r\n", trim($headerText));
$statusLine = array_shift($headerLines);
foreach ($headerLines as $line)
{
list($key, $value) = explode(':', $line, 2);
$headers[$key] = trim($value);
}
$statusLineComponents = explode(' ', $statusLine, 3);
$httpVersion = $statusLineComponents[0];
$reason = count($statusLineComponents) == 3 ? $statusLineComponents[2] : '';
$response = new Splunk_HttpResponse(array(
'status' => $status,
'reason' => $reason,
'headers' => $headers,
'body' => $body,
));
if ($status >= 400)
throw new Splunk_HttpException($response);
else
return $response;
}
}

@ -0,0 +1,64 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Thrown when an HTTP request fails due to a non 2xx status code.
*
* @package Splunk
*/
class Splunk_HttpException extends Splunk_IOException
{
private $response;
// === Init ===
/** @internal */
public function __construct($response)
{
$detail = Splunk_HttpException::parseFirstMessageFrom($response);
$message = "HTTP {$response->status} {$response->reason}";
if ($detail != NULL)
$message .= ' -- ' . $detail;
$this->response = $response;
parent::__construct($message);
}
/** Parses an HTTP response. */
private static function parseFirstMessageFrom($response)
{
if ($response->body == '')
return NULL;
return Splunk_XmlUtil::getTextContentAtXpath(
new SimpleXMLElement($response->body),
'/response/messages/msg');
}
// === Accessors ===
/**
* Gets an HTTP response.
*
* @return Splunk_HttpResponse
*/
public function getResponse()
{
return $this->response;
}
}

@ -0,0 +1,97 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Represents a response received from an HTTP request.
*
* When reading a potentially large response, the 'bodyStream'
* property should be used used in preference to the 'body' property,
* since it will only keep the current part of the body loaded in memory.
*
* @package Splunk
* @internal
*
* @property-read integer $status HTTP status code (ex: 200).
* @property-read string $reason HTTP reason string (ex: 'OK').
* @property-read array $headers Dictionary of headers.
* (ex: array('Content-Length' => '0')).
* @property-read string $body Content of the response,
* as a single byte string.
* @property-read resource $bodyStream
* Content of the response, as a stream
* (of the type returned by fopen()).
*/
class Splunk_HttpResponse
{
private $state;
private $body; // lazy
private $bodyStream; // lazy
/* @internal */
public function __construct($state)
{
$this->state = $state;
$this->body = NULL;
$this->bodyStream = NULL;
}
// === Accessors ===
/** @internal */
public function __get($key)
{
if ($key === 'body')
return $this->getBody();
else if ($key === 'bodyStream')
return $this->getBodyStream();
else
return $this->state[$key];
}
private function getBody()
{
if (array_key_exists('body', $this->state))
return $this->state['body'];
if ($this->body === NULL)
{
if (!array_key_exists('bodyStream', $this->state))
throw new Splunk_UnsupportedOperationException(
'Response object does not contain body stream.');
$this->body = Splunk_Util::stream_get_contents(
$this->state['bodyStream']);
}
return $this->body;
}
private function getBodyStream()
{
if (array_key_exists('bodyStream', $this->state))
return $this->state['bodyStream'];
if ($this->bodyStream === NULL)
{
if (!array_key_exists('body', $this->state))
throw new Splunk_UnsupportedOperationException(
'Response object does not contain body.');
$this->bodyStream = Splunk_StringStream::create($this->state['body']);
}
return $this->bodyStream;
}
}

@ -0,0 +1,23 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Thrown when an I/O error occurs.
*
* @package Splunk
*/
class Splunk_IOException extends Exception {}

@ -0,0 +1,87 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* An index contains events that have been logged to Splunk.
*
* @package Splunk
*/
class Splunk_Index extends Splunk_Entity
{
/**
* Logs one or more events to this index.
*
* It is highly recommended to specify a sourcetype explicitly.
*
* It is slightly faster to use {@link Splunk_Receiver::submit()}
* to accomplish the same task. One fewer network request is needed.
*
* @param string $data Raw event text.
* This may contain data for multiple events.
* Under the default configuration, line breaks
* ("\n") can be inserted to separate multiple events.
* @param array $args (optional) {<br/>
* **host**: (optional) The value to populate in the host field
* for events from this data input.<br/>
* **host_regex**: (optional) A regular expression used to
* extract the host value from each event.<br/>
* **source**: (optional) The source value to fill in the
* metadata for this input's events.<br/>
* **sourcetype**: (optional) The sourcetype to apply to
* events from this input.<br/>
* }
* @throws Splunk_IOException
* @link http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTinput#receivers.2Fsimple
*/
public function submit($data, $args=array())
{
$this->service->getReceiver()->submit($data, array_merge($args, array(
'index' => $this->getName(),
)));
}
/**
* Creates a stream for logging events to the specified index.
*
* It is highly recommended to specify a sourcetype explicitly.
*
* It is slightly faster to use {@link Splunk_Receiver::attach()}
* to accomplish the same task. One fewer network request is needed.
*
* The returned stream should eventually be closed via fclose().
*
* @param array $args (optional) {<br/>
* **host**: (optional) The value to populate in the host field
* for events from this data input.<br/>
* **host_regex**: (optional) A regular expression used to
* extract the host value from each event.<br/>
* **source**: (optional) The source value to fill in the
* metadata for this input's events.<br/>
* **sourcetype**: (optional) The sourcetype to apply to
* events from this input.<br/>
* }
* @return resource A stream that you can write event text to.
* @throws Splunk_IOException
* @link http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTinput#receivers.2Fstream
*/
public function attach($args=array())
{
return $this->service->getReceiver()->attach(array_merge($args, array(
'index' => $this->getName(),
)));
}
}

@ -0,0 +1,413 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Represents a running or completed search job.
*
* @package Splunk
*/
class Splunk_Job extends Splunk_Entity
{
// NOTE: These constants are somewhat arbitrary and could use some tuning
const DEFAULT_FETCH_MAX_TRIES = 10;
const DEFAULT_FETCH_DELAY_PER_RETRY = 0.1; // secs
// === Load ===
/*
* Job requests sometimes yield an HTTP 204 code when they are in the
* process of being created. To hide this from the caller, transparently
* retry requests when an HTTP 204 is received.
*/
protected function fetch($fetchArgs)
{
$fetchArgs = array_merge(array(
'maxTries' => Splunk_Job::DEFAULT_FETCH_MAX_TRIES,
'delayPerRetry' => Splunk_Job::DEFAULT_FETCH_DELAY_PER_RETRY,
), $fetchArgs);
for ($numTries = 0; $numTries < $fetchArgs['maxTries']; $numTries++)
{
$response = parent::fetch($fetchArgs);
if ($this->isFullResponse($response))
return $response;
usleep($fetchArgs['delayPerRetry'] * 1000000);
}
// Give up
throw new Splunk_HttpException($response);
}
protected function extractEntryFromRootXmlElement($xml)
{
// <entry> element is at the root of a job's Atom feed
return $xml;
}
// === Ready ===
/**
* Returns a value that indicates whether this job has been loaded.
*
* @return bool Whether this job has been loaded.
*/
public function isReady()
{
return $this->isLoaded();
}
/**
* Loads this job, retrying the specified number of times as necessary.
*
* @param int $maxTries The maximum number of times to try loading
* this job.
* @param float $delayPerRetry The number of seconds to wait between
* attempts to retry loading this job.
* @return Splunk_Entity This entity.
* @throws Splunk_IOException
*/
public function makeReady(
$maxTries=Splunk_Job::DEFAULT_FETCH_MAX_TRIES,
$delayPerRetry=Splunk_Job::DEFAULT_FETCH_DELAY_PER_RETRY)
{
return $this->validate(/*fetchArgs=*/array(
'maxTries' => $maxTries,
'delayPerRetry' => $delayPerRetry,
));
}
// === Accessors ===
// Overrides superclass to return the correct ID of this job,
// which can be used to lookup this job from the Jobs collection.
/**
* @see Splunk_Entity::getName()
*/
public function getName()
{
return $this['sid'];
}
/**
* Returns the search string executed by this job.
*
* @return string The search string executed by this job.
*/
public function getSearch()
{
return $this->getTitle();
}
// === Results ===
/**
* Returns a value that indicates the percentage of this job's results
* that were computed at the time this job was last loaded or
* refreshed.
*
* @return float Percentage of this job's results that were
* computed (0.0-1.0) at the time this job was
* last loaded or refreshed.
* @see Splunk_Entity::refresh()
*/
public function getProgress()
{
return floatval($this['doneProgress']);
}
/**
* Returns a value that indicates whether this job's results were available
* at the time this job was last loaded or refreshed.
*
* @return boolean Whether this job's results were available
* at the time this job was last loaded or
* refreshed.
* @see Splunk_Entity::refresh()
*/
public function isDone()
{
return ($this['isDone'] === '1');
}
/**
* Returns an iterator over the results from this job.
*
* Large result sets will be paginated automatically.
*
* Example:
*
* <pre>
* $job = ...;
* while (!$job->refresh()->isDone()) { usleep(0.5 * 1000000); }
*
* foreach ($job->getResults() as $result)
* {
* // (See documentation for Splunk_ResultsReader to see how to
* // interpret $result.)
* ...
* }
* </pre>
*
* This method cannot be used to access results from realtime jobs,
* which are never done. Use {@link getResultsPreviewPage()} instead.
*
* @param array $args (optional) {<br/>
* **count**: (optional) The maximum number of results to return,
* or -1 to return as many as possible.
* Defaults to returning as many as possible.<br/>
* **offset**: (optional) The offset of the first result to return.
* Defaults to 0.<br/>
* **pagesize**: (optional) The number of results to fetch from the
* server on each request when paginating internally,
* or -1 to return as many results as possible.
* Defaults to returning as many results as possible.<br/>
*
* **field_list**: (optional) Comma-separated list of fields to return
* in the result set. Defaults to all fields.<br/>
* **output_mode**: (optional) The output format of the result. Valid
* values:<br/>
* - "csv"<br/>
* - "raw"<br/>
* - "xml": The format parsed by Splunk_ResultsReader.
* <br/>
* - "json"<br/>
* Defaults to "xml".<br/>
* You should not change this unless you are parsing
* results yourself.<br/>
* **search**: (optional) The post processing search to apply to
* results. Can be any valid search language string.
* For example "search sourcetype=splunkd" will match any
* result whose "sourcetype" field is "splunkd".<br/>
* }
* @return Iterator The results (i.e. transformed events)
* of this job, via an iterator.
* @throws Splunk_JobNotDoneException
* If the results are not ready yet.
* Check isDone() to ensure the results are
* ready prior to calling this method.
* @throws Splunk_IOException
* @link http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs.2F.7Bsearch_id.7D.2Fresults
*/
public function getResults($args=array())
{
return new Splunk_PaginatedResultsReader($this, $args);
}
/**
* Returns a single page of results from this job.
*
* Most potential callers should use {@link getResults()} instead.
* Only use this method if you wish to parse job results yourself
* or want to control pagination manually.
*
* By default, all results are returned. For large
* result sets, it is advisable to fetch items using multiple calls with
* the paging options (i.e. 'offset' and 'count').
*
* The format of the results depends on the 'output_mode' argument
* (which defaults to "xml"). XML-formatted results can be parsed
* using {@link Splunk_ResultsReader}. For example:
*
* <pre>
* $job = ...;
* while (!$job->refresh()->isDone()) { usleep(0.5 * 1000000); }
*
* $results = new Splunk_ResultsReader($job->getResultsPage());
* foreach ($results as $result)
* {
* // (See documentation for Splunk_ResultsReader to see how to
* // interpret $result.)
* ...
* }
* </pre>
*
* This method cannot be used to access results from realtime jobs,
* which are never done. Use {@link getResultsPreviewPage()} instead.
*
* @param array $args (optional) {<br/>
* **count**: (optional) The maximum number of results to return,
* or -1 to return as many as possible.
* Defaults to returning as many as possible.<br/>
* **offset**: (optional) The offset of the first result to return.
* Defaults to 0.<br/>
*
* **field_list**: (optional) Comma-separated list of fields to return
* in the result set. Defaults to all fields.<br/>
* **output_mode**: (optional) The output format of the result. Valid
* values:<br/>
* - "csv"<br/>
* - "raw"<br/>
* - "xml": The format parsed by Splunk_ResultsReader.
* <br/>
* - "json"<br/>
* Defaults to "xml".<br/>
* You should not change this unless you are parsing
* results yourself.<br/>
* **search**: (optional) The post processing search to apply to
* results. Can be any valid search language string.
* For example "search sourcetype=splunkd" will match any
* result whose "sourcetype" field is "splunkd".<br/>
* }
* @return resource The results (i.e. transformed events)
* of this job, as a stream.
* @throws Splunk_JobNotDoneException
* If the results are not ready yet.
* Check isDone() to ensure the results are
* ready prior to calling this method.
* @throws Splunk_IOException
* @link http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs.2F.7Bsearch_id.7D.2Fresults
*/
public function getResultsPage($args=array())
{
$response = $this->fetchPage('results', $args);
if ($response->status == 204)
throw new Splunk_JobNotDoneException($response);
return $response->bodyStream;
}
/**
* Returns a single page of results from this job,
* which may or may not be done running.
*
* @param array $args (optional) {<br/>
* **count**: (optional) The maximum number of results to return,
* or -1 to return as many as possible.
* Defaults to returning as many as possible.<br/>
* **offset**: (optional) The offset of the first result to return.
* Defaults to 0.<br/>
*
* **field_list**: (optional) Comma-separated list of fields to return
* in the result set. Defaults to all fields.<br/>
* **output_mode**: (optional) The output format of the result. Valid
* values:<br/>
* - "csv"<br/>
* - "raw"<br/>
* - "xml": The format parsed by Splunk_ResultsReader.
* <br/>
* - "json"<br/>
* Defaults to "xml".<br/>
* You should not change this unless you are parsing
* results yourself.<br/>
* **search**: (optional) The post processing search to apply to
* results. Can be any valid search language string.
* For example "search sourcetype=splunkd" will match any
* result whose "sourcetype" field is "splunkd".<br/>
* }
* @return resource The results (i.e. transformed events)
* of this job, as a stream.
* @throws Splunk_IOException
* @link http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs.2F.7Bsearch_id.7D.2Fresults_preview
*/
public function getResultsPreviewPage($args=array())
{
$response = $this->fetchPage('results_preview', $args);
if ($response->status == 204)
{
// The REST API throws a 204 when a preview is being generated
// and no results are available. This isn't a friendly behavior
// for clients.
return Splunk_StringStream::create('');
}
return $response->bodyStream;
}
/** Fetches a page of the specified type. */
private function fetchPage($pageType, $args)
{
$args = array_merge(array(
'count' => -1,
), $args);
if ($args['count'] <= 0 && $args['count'] != -1)
throw new InvalidArgumentException(
'Count must be positive or -1 (infinity).');
if ($args['count'] == -1)
$args['count'] = 0; // infinity value for the REST API
$response = $this->sendGet("/{$pageType}", $args);
return $response;
}
/** Determines whether a response contains full or partial results */
private function isFullResponse($response)
{
if ($response->status == 204)
$result = FALSE;
else
{
$responseBody = new SimpleXMLElement($response->body);
$dispatchState = implode($responseBody->content->xpath('s:dict/s:key[@name="dispatchState"]/text()'));
$result = !($dispatchState === 'QUEUED' || $dispatchState === 'PARSING');
}
return $result;
}
// === Control ===
/**
* Pauses this search job.
*
* @throws Splunk_IOException
*/
public function pause()
{
$this->sendControlAction('pause');
}
/**
* Unpauses this search job.
*
* @throws Splunk_IOException
*/
public function unpause()
{
$this->sendControlAction('unpause');
}
/**
* Stops this search job but keeps the partial results.
*
* @throws Splunk_IOException
*/
public function finalize()
{
$this->sendControlAction('finalize');
}
/**
* Stops this search job and deletes the results.
*
* @throws Splunk_IOException
*/
public function cancel()
{
$this->sendControlAction('cancel');
}
/**
* Posts the specified control action.
*
* @throws Splunk_IOException
*/
private function sendControlAction($actionName)
{
$response = $this->sendPost('/control', array(
'action' => $actionName,
));
}
}

@ -0,0 +1,26 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Thrown when an attempt is made to access a job's results or events
* before the job is done.
*
* @package Splunk
*/
class Splunk_JobNotDoneException extends Splunk_HttpException
{
}

@ -0,0 +1,107 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Represents the collection of all search jobs.
*
* @package Splunk
*/
class Splunk_Jobs extends Splunk_Collection
{
// === Operations ===
/**
* @see Splunk_Collection::get()
*/
public function get($name, $namespace=NULL)
{
$this->checkName($name);
$this->checkNamespace($namespace);
// Delegate to Job, which already has the special handling to
// fetch an individual Job entity.
return $this->getReference($name, $namespace)->makeReady();
}
/**
* Creates a new search job.
*
* @param string $search The search query for the job to perform.
* @param array $args (optional) Job-specific creation arguments,
* merged with {<br/>
* **namespace**: (optional) {Splunk_Namespace} The namespace in which
* to create the entity. Defaults to the service's
* namespace.<br/>
* }<br/>
* For details, see the
* <a href="http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs">
* "POST search/jobs"</a>
* endpoint in the REST API Documentation.
* @return Splunk_Job
* @throws Splunk_IOException
*/
public function create($search, $args=array())
{
$args = array_merge(array(
'search' => $search,
), $args);
if (array_key_exists('exec_mode', $args) && ($args['exec_mode'] === 'oneshot'))
throw new InvalidArgumentException(
'Cannot create oneshot jobs with this method. Use createOneshot() instead.');
$namespace = Splunk_Util::getArgument($args, 'namespace', NULL);
$response = $this->sendPost('', $args);
$xml = new SimpleXMLElement($response->body);
$sid = Splunk_XmlUtil::getTextContentAtXpath($xml, '/response/sid');
return $this->getReference($sid, $namespace);
}
/**
* Executes the specified search query and returns results immediately.
*
* @param string $search The search query for the job to perform.
* @param array $args (optional) Job-specific creation arguments,
* merged with {<br/>
* **namespace**: (optional) {Splunk_Namespace} The namespace in which
* to create the entity. Defaults to the service's
* namespace.<br/>
* }<br/>
* For details, see the
* <a href="http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs">
* "POST search/jobs"</a>
* endpoint in the REST API Documentation.
* @return string The search results, which can be parsed with
* Splunk_ResultsReader.
* @throws Splunk_IOException
*/
public function createOneshot($search, $args=array())
{
$args = array_merge(array(
'search' => $search,
'exec_mode' => 'oneshot',
), $args);
if ($args['exec_mode'] !== 'oneshot')
throw new InvalidArgumentException(
'Cannot override "exec_mode" with value other than "oneshot".');
$response = $this->sendPost('', $args);
return $response->body;
}
}

@ -0,0 +1,254 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
// NOTE: Ideally the static constructors for this class wouldn't have any
// prefix (like 'create') or suffix. Unfortunately both 'default' and
// 'global' are considered keywords in PHP, preventing their use
// as static constructor names.
/**
* Represents a namespace. Every Splunk object belongs to a namespace.
*
* @package Splunk
*/
class Splunk_Namespace
{
private $owner;
private $app;
private $sharing;
// === Init ===
/** Constructs a new namespace with the specified parameters. */
private function __construct($owner, $app, $sharing)
{
$this->owner = $owner;
$this->app = $app;
$this->sharing = $sharing;
}
/**
* Creates the default namespace.
*
* Objects in the default namespace correspond to the authenticated user
* and their default Splunk application.
*
* @return Splunk_Namespace
*/
public static function createDefault()
{
$numArgs = func_num_args(); // must be own line for PHP < 5.3.0
Splunk_Namespace::ensureArgumentCountEquals(0, $numArgs);
static $defaultNamespace = NULL;
if ($defaultNamespace === NULL)
$defaultNamespace = new Splunk_Namespace(NULL, NULL, 'default');
return $defaultNamespace;
}
/**
* Creates the namespace containing objects associated with the specified
* user and application.
*
* @param string|NULL $owner name of a Splunk user (ex: "admin"),
* or NULL to specify all users.
* @param string|NULL $app name of a Splunk app (ex: "search"),
* or NULL to specify all apps.
* @return Splunk_Namespace
*/
public static function createUser($owner, $app)
{
$numArgs = func_num_args(); // must be own line for PHP < 5.3.0
Splunk_Namespace::ensureArgumentCountEquals(2, $numArgs);
if ($owner === '' || $owner === 'nobody' || $owner === '-')
throw new InvalidArgumentException('Invalid owner.');
if ($app === '' || $app === 'system' || $app === '-')
throw new InvalidArgumentException('Invalid app.');
if ($owner === NULL)
$owner = '-';
if ($app === NULL)
$app = '-';
return new Splunk_Namespace($owner, $app, 'user');
}
/**
* Creates the non-global namespace containing objects associated with the
* specified application.
*
* @param string|NULL $app name of a Splunk app (ex: "search"),
* or NULL to specify all apps.
* @return Splunk_Namespace
*/
public static function createApp($app)
{
$numArgs = func_num_args(); // must be own line for PHP < 5.3.0
Splunk_Namespace::ensureArgumentCountEquals(1, $numArgs);
if ($app === '' || $app === 'system' || $app === '-')
throw new InvalidArgumentException('Invalid app.');
if ($app === NULL)
$app = '-';
return new Splunk_Namespace('nobody', $app, 'app');
}
/**
* Creates the global namespace containing objects associated with the
* specified application.
*
* @param string|NULL $app name of a Splunk app (ex: "search"),
* or NULL to specify all apps.
* @return Splunk_Namespace
*/
public static function createGlobal($app)
{
$numArgs = func_num_args(); // must be own line for PHP < 5.3.0
Splunk_Namespace::ensureArgumentCountEquals(1, $numArgs);
if ($app === '' || $app === 'system' || $app === '-')
throw new InvalidArgumentException('Invalid app.');
if ($app === NULL)
$app = '-';
return new Splunk_Namespace('nobody', $app, 'global');
}
/**
* Creates the system namespace.
*
* Objects in the system namespace ship with Splunk.
*
* @return Splunk_Namespace
*/
public static function createSystem()
{
$numArgs = func_num_args(); // must be own line for PHP < 5.3.0
Splunk_Namespace::ensureArgumentCountEquals(0, $numArgs);
static $system = NULL;
if ($system === NULL)
$system = new Splunk_Namespace('nobody', 'system', 'system');
return $system;
}
/**
* Creates a non-wildcarded namespace with the specified properties.
*
* @param string $owner name of a Splunk user (ex: "admin").
* @param string $app name of a Splunk app (ex: "search").
* @param string $sharing one of {'user', 'app', 'global', 'system'}.
* @see user()
*/
public static function createExact($owner, $app, $sharing)
{
$numArgs = func_num_args(); // must be own line for PHP < 5.3.0
Splunk_Namespace::ensureArgumentCountEquals(3, $numArgs);
if (!in_array($sharing, array('user', 'app', 'global', 'system')))
throw new InvalidArgumentException('Invalid sharing.');
if ($owner === NULL || $owner === '' || $owner === '-')
throw new InvalidArgumentException('Invalid owner.');
if ($app === NULL || $app === '' || $app === '-')
throw new InvalidArgumentException('Invalid app.');
return new Splunk_Namespace($owner, $app, $sharing);
}
// === Accessors ===
/** Returns the path prefix to use when referencing objects in this
namespace. */
public function getPathPrefix()
{
switch ($this->sharing)
{
case 'default':
return '/services/';
case 'user':
case 'app':
case 'global':
case 'system':
return '/servicesNS/' . urlencode($this->owner) . '/' . urlencode($this->app) . '/';
default:
throw new Exception("Invalid sharing mode '{$this->sharing}'.");
}
}
/**
* Returns whether this is an exact (non-wildcarded) namespace.
*
* Within an exact namespace, no two objects can have the same name.
*/
public function isExact()
{
return ($this->owner !== '-') && ($this->app !== '-');
}
/**
* Returns the user who owns objects in this namespace.
*
* This operation is only defined for exact namespaces.
*/
public function getOwner()
{
$this->ensureExact();
return $this->owner;
}
/**
* Returns the app associated with objects in this namespace.
*
* This operation is only defined for exact namespaces.
*/
public function getApp()
{
$this->ensureExact();
return $this->app;
}
/**
* Returns the sharing mode of this namespace.
*
* This operation is only defined for exact namespaces.
*/
public function getSharing()
{
$this->ensureExact();
return $this->sharing;
}
// === Utility ===
// (Explicitly check the argument count because many creation function
// names do not make the required number of arguments clear and PHP
// does not check under certain circumstances.)
/** Throws an exception if the number of arguments is not what was
expected. */
private static function ensureArgumentCountEquals($expected, $actual)
{
if ($actual !== $expected)
throw new InvalidArgumentException(
"Expected exactly ${expected} arguments.");
}
/** Throws an exception if this namespace is not an exact (non-wildcarded)
namespace. */
private function ensureExact()
{
if (!$this->isExact())
throw new Splunk_UnsupportedOperationException(
'This operation is supported only for exact namespaces.');
}
}

@ -0,0 +1,30 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Thrown when an attempt is made to access a Splunk entity that does not exist.
*
* @package Splunk
*/
class Splunk_NoSuchEntityException extends RuntimeException
{
/** @internal */
public function __construct($name)
{
parent::__construct("No entity exists with name '{$name}'.");
}
}

@ -0,0 +1,170 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* @package Splunk
* @internal
*/
class Splunk_PaginatedResultsReader implements Iterator
{
private $job;
private $args;
private $curPageResultsIterator;
private $curOffset;
private $limOffset;
private $pageMaxSize;
private $fieldOrderWasReturned;
private $currentElement;
private $atStart;
/**
* Do not instantiate this class directly.
* Please call Splunk_Job::getResults() instead.
*/
public function __construct($job, $args)
{
list($args, $pageMaxSize) =
Splunk_Util::extractArgument($args, 'pagesize', -1);
list($args, $offset) =
Splunk_Util::extractArgument($args, 'offset', 0);
list($args, $count) =
Splunk_Util::extractArgument($args, 'count', -1);
if ($pageMaxSize <= 0 && $pageMaxSize != -1)
throw new InvalidArgumentException(
'Page size must be positive or -1 (infinity).');
if ($offset < 0)
throw new InvalidArgumentException(
'Offset must be >= 0.');
if ($count <= 0 && $count != -1)
throw new InvalidArgumentException(
'Count must be positive or -1 (infinity).');
// (Use PHP_INT_MAX for infinity internally because it works
// well with the min() function.)
if ($pageMaxSize == -1)
$pageMaxSize = PHP_INT_MAX; // internal infinity value
if ($count == -1)
$count = PHP_INT_MAX; // internal infinity value
$this->job = $job;
$this->args = $args;
$this->curPageResults = NULL;
$this->curOffset = $offset;
$this->limOffset = ($count == PHP_INT_MAX) ? PHP_INT_MAX : ($offset + $count);
$this->pageMaxSize = $pageMaxSize;
$this->fieldOrderWasReturned = FALSE;
$this->currentElement = $this->readNextElement();
$this->atStart = TRUE;
}
// === Iterator Methods ===
public function rewind()
{
if ($this->atStart)
return;
throw new Splunk_UnsupportedOperationException(
'Cannot rewind after reading past the first element.');
}
public function valid()
{
return ($this->currentElement !== NULL);
}
public function next()
{
$this->currentElement = $this->readNextElement();
$this->atStart = FALSE;
}
public function current()
{
return $this->currentElement;
}
public function key()
{
return NULL;
}
// === Read Next Element ===
private function readNextElement()
{
if ($this->curPageResultsIterator == NULL ||
!$this->curPageResultsIterator->valid())
{
if ($this->curOffset >= $this->limOffset)
{
return NULL; // at EOF
}
$numRemaining = ($this->limOffset == PHP_INT_MAX)
? PHP_INT_MAX
: ($this->limOffset - $this->curOffset);
$curPageMaxSize = min($this->pageMaxSize, $numRemaining);
if ($curPageMaxSize == PHP_INT_MAX)
$curPageMaxSize = -1; // infinity value for getResultsPage()
$this->curPageResultsIterator = new Splunk_ResultsReader(
$this->job->getResultsPage(
array_merge($this->args, array(
'offset' => $this->curOffset,
'count' => $curPageMaxSize,
'output_mode' => 'xml',
))));
if (!$this->curPageResultsIterator->valid())
{
$this->limOffset = $this->curOffset; // remember EOF position
return NULL; // at EOF
}
}
assert($this->curPageResultsIterator->valid());
$element = $this->curPageResultsIterator->current();
$this->curPageResultsIterator->next();
if ($element instanceof Splunk_ResultsFieldOrder)
{
// Only return the field order once.
if ($this->fieldOrderWasReturned)
{
// Don't return the field order again.
// Skip to the next element.
return $this->readNextElement();
}
else
{
$this->fieldOrderWasReturned = TRUE;
}
}
else if (is_array($element))
{
$this->curOffset++;
}
return $element;
}
}

@ -0,0 +1,127 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Provides methods for logging events to a Splunk index.
*
* @package Splunk
*/
class Splunk_Receiver
{
private $service;
/** @internal */
public function __construct($service)
{
$this->service = $service;
}
// === Operations ===
/**
* Logs one or more events to the specified index.
*
* In addition to the index name it is highly recommended to specify
* a sourcetype explicitly.
*
* @param string $data Raw event text.
* This may contain data for multiple events.
* Under the default configuration, line breaks
* ("\n") can be inserted to separate multiple events.
* @param array $args (optional) {<br/>
* **host**: (optional) The value to populate in the host field
* for events from this data input.<br/>
* **host_regex**: (optional) A regular expression used to
* extract the host value from each event.<br/>
* **index**: (optional) The index to send events from this
* input to. Highly recommended. Defaults to "default".<br/>
* **source**: (optional) The source value to fill in the
* metadata for this input's events.<br/>
* **sourcetype**: (optional) The sourcetype to apply to
* events from this input.<br/>
* }
* @throws Splunk_IOException
* @link http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTinput#receivers.2Fsimple
*/
public function submit($data, $args=array())
{
// (Avoid the normal post() method, since we aren't sending form data.)
$this->service->sendRequest(
'post', '/services/receivers/simple',
array('Content-Type' => 'text/plain'),
$data,
$args);
}
/**
* Creates a stream for logging events to the specified index.
*
* In addition to the index name it is highly recommended to specify
* a sourcetype explicitly.
*
* The returned stream should eventually be closed via fclose().
*
* @param array $args (optional) {<br/>
* **host**: (optional) The value to populate in the host field
* for events from this data input.<br/>
* **host_regex**: (optional) A regular expression used to
* extract the host value from each event.<br/>
* **index**: (optional) The index to send events from this
* input to. Highly recommended. Defaults to "default".<br/>
* **source**: (optional) The source value to fill in the
* metadata for this input's events.<br/>
* **sourcetype**: (optional) The sourcetype to apply to
* events from this input.<br/>
* }
* @return resource A stream that you can write event text to.
* @throws Splunk_IOException
* @link http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTinput#receivers.2Fstream
*/
public function attach($args=array())
{
$scheme = $this->service->getScheme();
$host = $this->service->getHost();
$port = $this->service->getPort();
$errno = 0;
$errstr = '';
if ($scheme == 'http')
$stream = @fsockopen($host, $port, /*out*/ $errno, /*out*/ $errstr);
else if ($scheme == 'https')
$stream = @fsockopen('ssl://' . $host, $port, /*out*/ $errno, /*out*/ $errstr);
else
throw new Splunk_UnsupportedOperationException(
'Unsupported URL scheme.');
if ($stream === FALSE)
throw new Splunk_ConnectException($errstr, $errno);
$path = '/services/receivers/stream?' . http_build_query($args);
$token = $this->service->getToken();
$headers = array(
"POST {$path} HTTP/1.1\r\n",
"Host: {$host}:{$port}\r\n",
"Accept-Encoding: identity\r\n",
"Authorization: {$token}\r\n",
"X-Splunk-Input-Mode: Streaming\r\n",
"\r\n",
);
Splunk_Util::fwriteall($stream, implode('', $headers));
return $stream;
}
}

@ -0,0 +1,45 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Represents a statement of which fields will be returned in a results stream,
* and what their relative ordering is.
*
* @package Splunk
* @see Splunk_ResultsReader
*/
class Splunk_ResultsFieldOrder
{
private $fieldNames;
/** @internal */
public function __construct($fieldNames)
{
$this->fieldNames = $fieldNames;
}
/**
* Gets an ordered list of field names that will be returned in the results stream.
*
* @return array A ordered list of the field names that will be returned
* in the results stream.
*/
public function getFieldNames()
{
return $this->fieldNames;
}
}

@ -0,0 +1,55 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Represents a message received in a results stream.
*
* @package Splunk
* @see Splunk_ResultsReader
*/
class Splunk_ResultsMessage
{
private $type;
private $text;
/** @internal */
public function __construct($type, $text)
{
$this->type = $type;
$this->text = $text;
}
/**
* Gets the type of this message.
*
* @return string The type of this message (ex: 'DEBUG').
*/
public function getType()
{
return $this->type;
}
/**
* Gets the text of this message.
*
* @return string The text of this message.
*/
public function getText()
{
return $this->text;
}
}

@ -0,0 +1,314 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Parses XML search results received from jobs.
*
* Results are obtained by iterating over an instance of this class
* using a foreach loop. Each result can be a Splunk_ResultsFieldOrder,
* a Splunk_ResultsMessage, an associative array, or potentially instances
* of other classes in the future.
*
* If the result is an associative array, it maps each
* field name to either a single value or an array of values.
*
* <pre>
* $resultsReader = new Splunk_ResultsReader(...);
* foreach ($resultsReader as $result)
* {
* if ($result instanceof Splunk_ResultsFieldOrder)
* {
* // Process the field order
* print "FIELDS: " . implode(',', $result->getFieldNames()) . "\r\n";
* }
* else if ($result instanceof Splunk_ResultsMessage)
* {
* // Process a message
* print "[{$result->getType()}] {$result->getText()}\r\n";
* }
* else if (is_array($result))
* {
* // Process a row
* print "{\r\n";
* foreach ($result as $key => $valueOrValues)
* {
* if (is_array($valueOrValues))
* {
* $values = $valueOrValues;
* $valuesString = implode(',', $values);
* print " {$key} => [{$valuesString}]\r\n";
* }
* else
* {
* $value = $valueOrValues;
* print " {$key} => {$value}\r\n";
* }
* }
* print "}\r\n";
* }
* else
* {
* // Ignore unknown result type
* }
* }
* </pre>
*
* @package Splunk
*/
class Splunk_ResultsReader implements Iterator
{
private $emptyXml;
private $xmlReader;
private $currentElement;
private $atStart;
/**
* Constructs a new search results string or stream.
*
* @param string|resource $streamOrXmlString
* A string or stream containing results obtained from the
* {@link Splunk_Job::getResultsPage()} method.
*/
public function __construct($streamOrXmlString)
{
if (is_string($streamOrXmlString))
{
$string = $streamOrXmlString;
$stream = Splunk_StringStream::create($string);
}
else
{
$stream = $streamOrXmlString;
}
// Search jobs lacking results return a blank document (with HTTP 200).
if (feof($stream))
{
$this->emptyXml = TRUE;
$this->currentElement = NULL;
$this->atStart = TRUE;
return;
}
else
{
$this->emptyXml = FALSE;
}
$streamUri = Splunk_StreamStream::createUriForStream($stream);
$this->xmlReader = new XMLReader();
$this->xmlReader->open($streamUri);
$this->currentElement = $this->readNextElement();
$this->atStart = TRUE;
}
// === Iterator Methods ===
/** @internal */
public function rewind()
{
if ($this->atStart)
return;
throw new Splunk_UnsupportedOperationException(
'Cannot rewind after reading past the first element.');
}
/**
* Returns a value that indicates whether there are any more elements in the stream.
*
* @return boolean Whether there are any more elements.
*/
public function valid()
{
return ($this->currentElement !== NULL);
}
/**
* Advances this iterator to the next element.
*/
public function next()
{
$this->currentElement = $this->readNextElement();
$this->atStart = FALSE;
}
/**
* Returns the current element of this iterator.
*
* @return Splunk_ResultsFieldOrder|Splunk_ResultsMessage|array|mixed
* The current element of this iterator.
*/
public function current()
{
return $this->currentElement;
}
/** @internal */
public function key()
{
return NULL;
}
// === Read Next Element ===
/** Returns the next element in the stream. */
private function readNextElement()
{
$xr = $this->xmlReader;
if ($this->emptyXml)
return NULL;
while ($xr->read())
{
// Read: /meta
if ($xr->nodeType == XMLReader::ELEMENT &&
$xr->name === 'meta')
{
return $this->readMeta();
}
// Read: /messages/msg
if ($xr->nodeType == XMLReader::ELEMENT &&
$xr->name === 'msg')
{
$type = $xr->getAttribute('type');
// Read: /messages/msg/[TEXT]
if (!$xr->read())
break;
assert ($xr->nodeType == XMLReader::TEXT);
$text = $xr->value;
return new Splunk_ResultsMessage($type, $text);
}
// Read: /result
if ($xr->nodeType == XMLReader::ELEMENT &&
$xr->name === 'result')
{
return $this->readResult();
}
}
return NULL;
}
/** Reads metadata from the stream. */
private function readMeta()
{
$xr = $this->xmlReader;
$insideFieldOrder = FALSE;
$fieldsNames = NULL;
while ($xr->read())
{
// Begin: /meta/fieldOrder
if ($xr->nodeType == XMLReader::ELEMENT &&
$xr->name === 'fieldOrder')
{
$insideFieldOrder = TRUE;
$fieldsNames = array();
}
// Read: /meta/fieldOrder/field/[TEXT]
if ($insideFieldOrder &&
$xr->nodeType == XMLReader::TEXT)
{
$fieldsNames[] = $xr->value;
}
// End: /meta/fieldOrder
if ($xr->nodeType == XMLReader::END_ELEMENT &&
$xr->name === 'fieldOrder')
{
return new Splunk_ResultsFieldOrder($fieldsNames);
}
}
throw new Exception('Syntax error in <meta> element.');
}
/** Returns search results from the stream. */
private function readResult()
{
$xr = $this->xmlReader;
$lastKey = NULL;
$lastValues = array();
$insideValue = FALSE;
$result = array();
while ($xr->read())
{
// Begin: /result/field
if ($xr->nodeType == XMLReader::ELEMENT &&
$xr->name === 'field')
{
$lastKey = $xr->getAttribute('k');
$lastValues = array();
}
// Begin: /result/field/value
// Begin: /result/field/v
if ($xr->nodeType == XMLReader::ELEMENT &&
($xr->name === 'value' || $xr->name === 'v'))
{
$insideValue = TRUE;
}
// Read: /result/field/value/text/[TEXT]
// Read: /result/field/v/[TEXT]
if ($insideValue &&
$xr->nodeType == XMLReader::TEXT)
{
$lastValues[] = $xr->value;
}
// End: /result/field/value
// End: /result/field/v
if ($xr->nodeType == XMLReader::END_ELEMENT &&
($xr->name === 'value' || $xr->name === 'v'))
{
$insideValue = FALSE;
}
// End: /result/field
if ($xr->nodeType == XMLReader::END_ELEMENT &&
$xr->name === 'field')
{
if (count($lastValues) === 1)
{
$lastValues = $lastValues[0];
}
$result[$lastKey] = $lastValues;
}
// End: /result
if ($xr->nodeType == XMLReader::END_ELEMENT &&
$xr->name === 'result')
{
break;
}
}
return $result;
}
}

@ -0,0 +1,47 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Represents a saved search.
*
* @package Splunk
*/
class Splunk_SavedSearch extends Splunk_Entity
{
// === Operations ===
/**
* Runs this saved search and returns the resulting search job.
*
* @param array $args (optional) Additional arguments.
* For details, see the
* <a href="http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#saved.2Fsearches.2F.7Bname.7D.2Fdispatch">
* "POST saved/searches/{name}/dispatch"</a>
* endpoint in the REST API Documentation.
* @return Splunk_Job
* The created search job.
*/
public function dispatch($args=array())
{
$response = $this->sendPost('/dispatch', $args);
$xml = new SimpleXMLElement($response->body);
$sid = Splunk_XmlUtil::getTextContentAtXpath($xml, '/response/sid');
return $this->service->getJobs()->getReference(
$sid, $this->getNamespace());
}
}

@ -0,0 +1,123 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Provides an object-oriented interface to access entities of a Splunk server.
*
* @package Splunk
*/
class Splunk_Service extends Splunk_Context
{
/**
* Constructs a new service with the specified parameters.
*
* @see Splunk_Context::__construct()
*/
public function __construct($args=array())
{
parent::__construct($args);
}
// === Endpoints ===
/**
* Gets the collection of indexes on this server.
*
* @return Splunk_Collection The collection of indexes on this server.
*/
public function getIndexes()
{
return new Splunk_Collection($this, 'data/indexes/', 'Splunk_Index');
}
/**
* Gets the collection of search jobs on this server.
*
* @return Splunk_Jobs The collection of search jobs on this server.
*/
public function getJobs()
{
return new Splunk_Jobs($this, 'search/jobs/', 'Splunk_Job');
}
/**
* Gets an interface to send events to this server.
*
* @return Splunk_Receiver An interface to send events to this server.
*/
public function getReceiver()
{
return new Splunk_Receiver($this);
}
/**
* Gets the collection of saved searches on this server.
*
* @return Splunk_Collection The collection of saved searches on this server.
*/
public function getSavedSearches()
{
return new Splunk_Collection($this, 'saved/searches/', 'Splunk_SavedSearch');
}
// === Convenience ===
/**
* Creates a new search job.
*
* @param string $search The search query for the job to perform.
* @param array $args (optional) Job-specific creation arguments,
* merged with {<br/>
* **namespace**: (optional) {Splunk_Namespace} The namespace in which
* to create the entity. Defaults to the service's
* namespace.<br/>
* }<br/>
* For details, see the
* <a href="http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs">
* "POST search/jobs"</a>
* endpoint in the REST API Documentation.
* @return Splunk_Job
* @throws Splunk_IOException
*/
public function search($search, $args=array())
{
return $this->getJobs()->create($search, $args);
}
/**
* Executes the specified search query and returns results immediately.
*
* @param string $search The search query for the job to perform.
* @param array $args (optional) Job-specific creation arguments,
* merged with {<br/>
* **namespace**: (optional) {Splunk_Namespace} The namespace in which
* to create the entity. Defaults to the service's
* namespace.<br/>
* }<br/>
* For details, see the
* <a href="http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTsearch#search.2Fjobs">
* "POST search/jobs"</a>
* endpoint in the REST API Documentation.
* @return string The search results, which can be parsed with
* Splunk_ResultsReader.
* @throws Splunk_IOException
*/
public function oneshotSearch($search, $args=array())
{
return $this->getJobs()->createOneshot($search, $args);
}
}

@ -0,0 +1,176 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* A stream wrapper implementation that reads from another underlying stream.
*
* This is useful to pass a stream as an input to a PHP API that only accepts
* URIs for its inputs, such as XMLReader.
*
* @package Splunk
* @internal
*/
class Splunk_StreamStream
{
// === Registration ===
private static $registeredStreams = array();
/**
* Makes the specified stream accessible by URI.
*
* @param resource $stream A stream created by fopen().
* @return string A URI that the provided stream can be
* opened with via another fopen() call.
* For example "splunkstream://5016d2d5c9d90".
*/
public static function createUriForStream($stream)
{
$streamId = uniqid();
Splunk_StreamStream::$registeredStreams[$streamId] = $stream;
return 'splunkstream://' . $streamId;
}
/**
* @return integer The number of streams that have been
* registered with createUriForStream()
* that haven't been closed.
*/
public static function getOpenStreamCount()
{
return count(Splunk_StreamStream::$registeredStreams);
}
// === Stream ===
private $stream;
private $streamId;
public function stream_open($path, $mode, $options, &$opened_path)
{
$url = parse_url($path);
$streamId = $url['host'];
if (array_key_exists($streamId, Splunk_StreamStream::$registeredStreams))
{
$this->stream = Splunk_StreamStream::$registeredStreams[$streamId];
$this->streamId = $streamId;
return TRUE;
}
else
{
return FALSE;
}
}
public function stream_read($count)
{
return fread($this->stream, $count);
}
public function stream_write($data)
{
return fwrite($this->stream, $data);
}
public function stream_seek($offset, $whence = SEEK_SET)
{
return fseek($this->stream, $offset, $whence);
}
public function stream_tell()
{
return ftell($this->stream);
}
public function stream_eof()
{
return feof($this->stream);
}
public function stream_stat()
{
return fstat($this->stream);
}
public function stream_flush()
{
return fflush($this->stream);
}
public function stream_close()
{
fclose($this->stream);
// (When called from a shutdown hook, sometimes this variable no
// longer exists when this method is called.)
if (isset(Splunk_StreamStream::$registeredStreams))
{
unset(Splunk_StreamStream::$registeredStreams[$this->streamId]);
}
}
public function url_stat($path, $flags)
{
$url = parse_url($path);
$streamId = $url['host'];
if (array_key_exists($streamId, Splunk_StreamStream::$registeredStreams))
{
$stream = Splunk_StreamStream::$registeredStreams[$streamId];
$statResult = fstat($stream);
if ($statResult === FALSE)
{
/*
* The API for url_stat() always requires a valid (non-FALSE),
* result, even though fstat() can return FALSE. The docs say
* to set unknown values to "a rational value (usually 0)".
*
* XMLReader::open() enforces this, printing out a cryptic
* "Unable to open source data" error if a FALSE result for
* url_stat() is returned.
*/
return array(
'dev' => 0,
'ino' => 0,
'mode' => 0,
'nlink' => 1,
'uid' => 0,
'gid' => 0,
'rdev' => -1,
'size' => 0,
'atime' => 0,
'mtime' => 0,
'ctime' => 0,
'blksize' => -1,
'blocks' => -1,
);
}
else
{
return $statResult;
}
}
else
{
return FALSE;
}
}
}
stream_wrapper_register('splunkstream', 'Splunk_StreamStream') or
die('Could not register protocol.');

@ -0,0 +1,51 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Creates a stream that reads from a string.
*
* @package Splunk
* @internal
*/
class Splunk_StringStream
{
/** (Prevent construction.) **/
private function __construct()
{
}
/**
* @return resource A stream that reads from the specified byte string.
*/
public static function create($string)
{
$stream = fopen('php://memory', 'rwb');
Splunk_Util::fwriteall($stream, $string);
fseek($stream, 0);
/*
* fseek() causes the next call to feof() to always return FALSE,
* which is undesirable if we seeked to the EOF. In this case,
* attempt a read past EOF so that the next call to feof() returns
* TRUE as expected.
*/
if ($string === '')
fread($stream, 1); // trigger EOF explicitly
return $stream;
}
}

@ -0,0 +1,25 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Thrown if the requested operation is not supported.
*
* @package Splunk
*/
class Splunk_UnsupportedOperationException extends RuntimeException
{
}

@ -0,0 +1,111 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Internal utility functions for the PHP SDK.
*
* @package Splunk
* @internal
*/
class Splunk_Util
{
/**
* Extracts the value for the specified $key from the specified $dict.
*
* @param array $dict
* @param mixed $key
* @param mixed $defaultValue
* @return array {
* [0] => $dict without $key
* [1] => $dict[$key] if it exists, or $defaultValue if it does not
* }
*/
public static function extractArgument($dict, $key, $defaultValue)
{
$value = array_key_exists($key, $dict) ? $dict[$key] : $defaultValue;
unset($dict[$key]);
return array($dict, $value);
}
/**
* Gets the value for the specified $key from the specified $dict,
* returning the $defaultValue in the key is not found.
*
* @param array $dict
* @param mixed $key
* @param mixed $defaultValue
* @return mixed
*/
public static function getArgument($dict, $key, $defaultValue)
{
return array_key_exists($key, $dict) ? $dict[$key] : $defaultValue;
}
/**
* Writes $data to $stream.
*
* @throws Splunk_IOException If an I/O error occurs.
*/
public static function fwriteall($stream, $data)
{
while (TRUE)
{
$numBytesWritten = fwrite($stream, $data);
if ($numBytesWritten === FALSE)
{
$errorInfo = error_get_last();
$errmsg = $errorInfo['message'];
$errno = $errorInfo['type'];
throw new Splunk_IOException($errmsg, $errno);
}
if ($numBytesWritten == strlen($data))
return;
$data = substr($data, $numBytesWritten);
}
}
/**
* Reads the entire contents of the specified stream.
* Throws a Splunk_IOException upon error.
*
* @param resource $stream A stream.
* @return string The contents of the specified stream.
* @throws Splunk_IOException If an I/O error occurs.
*/
public static function stream_get_contents($stream)
{
// HACK: Clear the last error
@trigger_error('');
$oldError = error_get_last();
$body = @stream_get_contents($stream);
$newError = error_get_last();
// HACK: Detecting whether stream_get_contents() has failed is not
// strightforward because it can either return FALSE or ''.
// However '' is also a legal return value in non-error scenarios.
if ($newError != $oldError)
{
$errorInfo = error_get_last();
$errmsg = $errorInfo['message'];
$errno = $errorInfo['type'];
throw new Splunk_IOException($errmsg, $errno);
}
return $body;
}
}

@ -0,0 +1,91 @@
<?php
/**
* Copyright 2013 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Utilities for manipulating SimpleXMLElement objects.
*
* @package Splunk
* @internal
*/
class Splunk_XmlUtil
{
/**
* Returns whether the specified XML element exists.
*
* @param SimpleXMLElement $xml
* @return boolean
*/
public static function elementExists($xml)
{
return $xml->getName() != '';
}
/**
* @param SimpleXMLElement $xml
* @param string $attributeName
* @return string|NULL
*/
public static function getAttributeValue($xml, $attributeName)
{
return (isset($xml->attributes()->$attributeName))
? (string) $xml->attributes()->$attributeName
: NULL;
}
/**
* @param SimpleXMLElement $xml
* @return string
*/
public static function getTextContent($xml)
{
// HACK: Some versions of PHP 5 can't access the [0] element
// of a SimpleXMLElement object properly.
return (string) $xml;
}
/**
* @param SimpleXMLElement $xml
* @param string $xpathExpr
* @return string|NULL
*/
public static function getTextContentAtXpath($xml, $xpathExpr)
{
$matchingElements = $xml->xpath($xpathExpr);
return (count($matchingElements) == 0)
? NULL
: Splunk_XmlUtil::getTextContent($matchingElements[0]);
}
/**
* Returns true if the specified SimpleXMLElement represents a unique
* element or false if it represents a collection of elements.
*
* @param SimpleXMLElement $xml
* @return bool
*/
public static function isSingleElement($xml)
{
$count = 0;
foreach ($xml as $item)
{
$count++;
if ($count >= 2)
return false;
}
return ($count == 1);
}
}

@ -0,0 +1,373 @@
<?php
//订单号 -> IP
//
//{
// "COLI_WebCode": "JP",
// "COLI_ID": "ZPL161028068",
// "COLI_ApplyDate": "2016-04-15 23:17:20",
// "COLI_SenderIP": "116.226.169.236"
//}
//
//CHT:http://www.chinahighlights.com/api/api.php?method=order.json_by_lmr&order_id=hy161104033
//国际:http://www.viaje-a-china.com/index.php/ajax/ajax_order_datas/
//
//
$order_id = isset($_GET['order_id']) ? $_GET['order_id'] : FALSE;
$order_id = isset($_POST['order_id']) ? $_POST['order_id'] : $order_id;
//$site = isset($_GET['site']) ? $_GET['site'] : FALSE;
//$site = isset($_POST['site']) ? $_POST['site'] : $site;
$ip = '255.255.255.255';
$web_code = FALSE;
$date = date('Y-m-d h:i:s');
$host = array(
'xx' => 'xx',
'jp' => 'JP',
'vc' => 'FR',
'vac' => 'ES',
'train_vac' => 'ES',
'ru' => 'RU',
'train_ru' => 'RU',
'it' => 'IT',
'train_it' => 'IT',
'cht' => 'CHT',
'ct' => 'yincheng',
'gm' => 'gm-vps',
'ah' => 'AH',
);
$host_name = array(
'jp' => 'http://www.arachina.com',
'vc' => 'http://www.voyageschine.com',
'vac' => 'http://www.viaje-a-china.com',
'train_vac' => 'http://www.viaje-a-china.com',
'ru' => 'http://www.chinahighlights.ru',
'train_ru' => 'http://www.chinahighlights.ru',
'it' => 'http://www.viaggio-in-cina.it',
'train_it' => 'http://www.viaggio-in-cina.it',
'cht' => 'http://www.chinahighlights.com',
'ct' => 'http://www.chinatravel.com',
'gm' => 'http://www.chinarundreisen.com',
'ah' => 'http://www.asiahighlights.com',
);
$bf_date = date('m/d/Y:00:00:00', time() - 3600*24*90);
$af_date = date('m/d/Y:00:00:00', time() + 3600*24*90);
$order_txt = '';
$web_code = 'xx';
$order_date = '';
if ($order_id)
{
//if ($site == 'ch')
//{
// $order = @file_get_contents('http://www.chinahighlights.com/api/api.php?method=order.json_by_lmr&order_id='.$order_id);
//}
//else
//{
$order = @file_get_contents('http://www.viaje-a-china.com/index.php/ajax/ajax_order_datas/'.$order_id);
//}
if ($order && $order !== 'no data')
{
$order = json_decode($order);
$ip = $order->COLI_SenderIP;
if (!$ip || $ip=='159.8.126.74' || $ip=='180.140.114.208')
{
$ip = null;
}
$web_code = $order->COLI_WebCode;
$order_date = $order->COLI_ApplyDate;
$date_time = strtotime($order->COLI_ApplyDate);
$bf_date = date('m/d/Y:00:00:00', $date_time - 3600*24*90);
$af_date = date('m/d/Y:00:00:00', $date_time + 3600*24*90);
$order_txt = $order->COLI_OrderDetailText;
}
}
//连接Splunk SDK
require_once '../Splunk.php';
$connect_arguments = array(
'scheme' => 'https',
'host' => '192.155.250.125',
'port' => 8089,
'username' => 'haina',
'password' => '383d43GZ82[Ai',
);
$service = new Splunk_Service($connect_arguments);
$service->login();
//查询IP
//[EXA] host=ES "68.180.229.232" earliest="11/1/2016:00:00:00" latest="11/2/2016:00:00:00"
$results = array();
if ($ip)
{
$search = 'search host='.$host[strtolower($web_code)].' "'.$ip.'" earliest="'.$bf_date.'" latest="'.$af_date.'" NOT("/ngx_pagespeed_beacon" OR "/tourprice" OR "/include" OR "/api" OR "/ajax" OR "/image" OR "/common" OR "/ckplayer" OR ".css" OR ".js" OR ".ico" OR ".gif" OR ".jpg" OR ".png" OR ".json" OR ".xml" OR ".ttf" OR ".woff" OR ".eot" OR "webhtmllog" OR "asphttp_accept_language" OR "down.asp" OR "verifyemail" OR "test")';
//调用Spluck Search
$job = $service->getJobs()->create($search);
while (!$job->isDone())
{
$job->refresh();
}
$results = $job->getResults();
}
?><!DOCTYPE html>
<html lang="en">
<head>
<title>订单日志查询</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link href="http://data.arachina.com/information-view/tools/min/?a&f=/bootstrap/css/bootstrap.min.css,/information/js/i18n/jquery.ui.datepicker/smoothness/jquery.min.css" rel="stylesheet" type="text/css"/>
<script src="http://data.arachina.com/information-view/tools/min/?a&f=/information/js/jquery.min.js,/information/js/jquery-migrate.js,/js/i18n/dp.js,/js/i18n/dp.ja.js,/information/js/jQuery.highlight.js"></script>
<style type="text/css">
@media (min-width: 768px) {
.container {width: 750px;}
._raw{max-width:700px;word-wrap: break-word;}
.btn-default {max-width: 700px;}
}
@media (min-width: 992px) {
.container {width: 970px;}
._raw{max-width:920px;word-wrap: break-word;}
.btn-default {max-width: 860px;}
}
@media (min-width: 1200px) {
.container {width: 1200px;}
._raw{max-width:1100px;word-wrap: break-word;}
.btn-default {max-width: 1000px;}
}
#formbody {margin:20px 0;}
#start_date, #end_date, #fz_name, #dz_name, #train_date {width: 135px;}
#site_code {min-width: 200px}
#train_date {min-width: 152px;}
#fix_alert {padding-top: 8px;padding-bottom: 8px;}
td,th {font-size: 13px;}
.pagination {margin: 0 0 12px;}
#ui-datepicker-div {z-index: 1000!important;}
pre {margin-top: 5px;font-size: 0.8em;}
.btn-default {white-space: initial;text-align: left;word-wrap: break-word;}
.tt{color: #07c;}
._raw a.btn-sm {font-size: 14px;}
</style>
</head>
<body class="container">
<div id="formbody">
<form class="form-inline" action="" method="post" id="sform">
<div class="form-group has-success">
<label class="sr-only" for="order_id">订单号</label>
<input type="text" name="order_id" class="form-control" id="order_id" placeholder="输入订单号" value="<?php if ($order_id) echo($order_id); ?>">
</div>
<button type="submit" class="btn btn-primary" data-loading-text="Loading..." id="btn6">查询</button>
</form>
</div>
<div class="alert alert-info" role="alert" id="fix_alert">
<?php
if ($web_code) echo(' [站点:'.$web_code.']');
if ($order_date) echo(' [提交日期:'.$order_date.']');
if ($order_txt) echo('<pre>'.$order_txt.'</pre>');
//echo(' 【SPL'.$search.'】');
?>
</div>
<!--nav>
<ul class="pagination">
<?php
$page = 1;
$page_count = 10;
$prev_cls = '';
if ($page == 1) $prev_cls = 'disabled';
$next_cls = '';
if ($page == $page_count) $next_cls = 'disabled';
?>
<li class="<?php echo($prev_cls);?>">
<a href="#" aria-label="Previous" id="prev">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<?php for ($i=1; $i<=$page_count; $i++) {
$cls = '';
if ($i == $page) $cls = 'active';
?>
<li class="<?php echo($cls);?>"><a href="javascript:;" class="page_a" data-action="/train/china-trains/search/logs/<?php echo($i);?>"><?php echo($i);?></a></li>
<?php } ?>
<li class="<?php echo($next_cls);?>">
<a href="#" aria-label="Next" id="next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav-->
<table class="table table-bordered table-hover">
<?php
$messages = array();
$anyRows = FALSE;
$columnNames = NULL;
foreach($results as $result)
{
if ($result instanceof Splunk_ResultsFieldOrder)
{
//所有字段
$columnNames = $result->getFieldNames();
//所需字段
$need_th = array('_serial', '_raw', '_time');
//字段差集
$columnNames = array_intersect($need_th, $columnNames);
echo '<thead><tr><td colspan="3" class="tt">提示: “GET”表示正常访问“POST”表示从表单提交访问“标黄”表示订单相关的访问。</td></tr><tr>';
foreach($columnNames as $columnName)
echo '<th>'.htmlspecialchars($columnName).
'</th>';
echo '</tr></thead>';
echo "\n";
}
else if ($result instanceof Splunk_ResultsMessage)
{
$messages[] = $result;
}
else if (is_array($result))
{
$anyRows = TRUE;
assert($columnNames !== NULL);
echo '<tr>';
foreach($columnNames as $columnName)
{
$cellValue = array_key_exists($columnName, $result) ? $result[$columnName] : NULL;
//nginx日志
if ($columnName == '_raw')
{
echo '<td><div class="_raw">';
if (is_array($cellValue))
{
$log = implode('', $cellValue);
//116.226.169.236 - - [27/Oct/2016:07:35:39 -0500] "GET / HTTP/1.1" 200 8857 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A456 Safari/602.1"
//ip(1), visit(3), from(6), ua(7)
$p = '/^(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s-\s-\s\[(.*)\]\s\"(.*)\"\s(\d{3})\s(\d+)\s\"(.*)\"\s\"(.*)\"$/';
preg_match($p, $log, $matches);
//print_r($matches);
if (isset($matches[3]))
{
$tmp_a = explode(' ', $matches[3]);
echo '<a href="'.$host_name[strtolower($web_code)].@$tmp_a[1].'" class="btn btn-sm btn-primary" type="button" target="_blank"><span class="badge">visit</span> '.urldecode($matches[3]).'</a> &nbsp;';
}
if (isset($matches[6]))
{
if ($matches[6]=='-') $matches[6] = '从收藏夹、历史记录或地址栏直接访问';
echo '<a href="'.$matches[6].'" class="btn btn-sm btn-default" type="button" target="_blank"><span class="badge">from</span> '.urldecode($matches[6]).'</a> &nbsp;';
}
if (!isset($matches[1]))
{
echo $log;
}
}
echo '</div></td>';
}
//日期
else if ($columnName == '_time')
{
echo '<td>';
$time = strtotime($cellValue);
echo date('Y-m-d h:i:s', $time);
echo '</td>';
}
//splunk记录
else
{
echo '<td>';
if ($cellValue !== NULL)
{
if (is_array($cellValue))
{
echo '<ul>';
foreach($cellValue as $value)
{
echo '<li>'.htmlspecialchars($value).'</li>';
}
echo '</ul>';
}
else
{
echo htmlspecialchars($cellValue);
}
}
echo '</td>';
}
}
echo '</tr>';
}
}
?>
</table>
<?php if (count($messages) > 0): ?>
<ul>
<?php
foreach ($messages as $message)
{
echo '<li>[' . htmlspecialchars($message->getType()) . '] ';
echo htmlspecialchars($message->getText()) . '</li>';
}
?>
</ul>
<?php endif; ?>
<?php if (!$anyRows && (count($messages) === 0)): ?>
<p>
没有查到相关日志
</p>
<?php endif; ?>
<script type="text/javascript">
function openwindow(url,name,iWidth,iHeight)
{
var url; //转向网页的地址;
var name; //网页名称,可为空;
var iWidth; //弹出窗口的宽度;
var iHeight; //弹出窗口的高度;
//window.screen.height获得屏幕的高window.screen.width获得屏幕的宽
var iTop = (window.screen.height-30-iHeight)/2; //获得窗口的垂直位置;
var iLeft = (window.screen.width-10-iWidth)/2; //获得窗口的水平位置;
window.open(url,name,'height='+iHeight+',,innerHeight='+iHeight+',width='+iWidth+',innerWidth='+iWidth+',top='+iTop+',left='+iLeft+',toolbar=no,menubar=no,scrollbars=auto,resizeable=no,location=no,status=no');
}
$('#start_date,#end_date,#train_date').datepicker({
changeMonth: true,
changeYear: true
});;
$('a.js_win').on('click', function() {
var _sn = $(this).attr('data-sn');
var _url = '/train/china-trains/search/log_data/'+_sn+'/';
openwindow(_url, "SL", 809, 500);
});
$('.page_a').on('click', function(){
$('#sform').attr('action', $(this).attr('data-action')).submit();
});
$('#prev').on('click', function() {
if (!$(this).parent('li').hasClass('disabled')) {
var curr = $('li.active').index();
$('#sform').attr('action', '/train/china-trains/search/logs/'+ (curr-1)).submit();
}
});
$('#next').on('click', function() {
if (!$(this).parent('li').hasClass('disabled')) {
var curr = $('li.active').index();
$('#sform').attr('action', '/train/china-trains/search/logs/'+ (curr+1)).submit();
}
});
$('#btn6').on('click', function () {
$(this).html('Seraching...');
$(this).prop('disabled', true);
$('#sform').submit();
})
//高亮
var css = {
color: 'black',
background: 'yellow',
ignoreCase: true,
wholeWord: false,
bold: false
};
if ($('table.table a').length) {
$('table.table a').highlight('orders', css);
$('table.table a').highlight('thankyou', css);
$('table.table a').highlight('save', css);
$('table.table a').highlight('forms', css);
}
</script>
</body>
</html>
Loading…
Cancel
Save