Skip to content
This repository has been archived by the owner on Nov 29, 2021. It is now read-only.

Commit

Permalink
Merge pull request #94 from philkra/add-span-support
Browse files Browse the repository at this point in the history
Support for Span Entity
  • Loading branch information
philkra authored Sep 5, 2019
2 parents ce157d0 + 123834f commit 09371df
Show file tree
Hide file tree
Showing 13 changed files with 348 additions and 64 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This is a community PHP agent for Elastic.co's [APM](https://www.elastic.co/solu

## Documentation
* [Installation](https://github.com/philkra/elastic-apm-php-agent/blob/master/docs/install.md)
* [Breaking Changes](https://github.com/philkra/elastic-apm-php-agent/blob/master/docs/breaking-changes.md)
* [Configuration](https://github.com/philkra/elastic-apm-php-agent/blob/master/docs/config.md)
* [Knowledgebase](https://github.com/philkra/elastic-apm-php-agent/blob/master/docs/knowledgebase.md)

Expand Down
5 changes: 5 additions & 0 deletions docs/breaking-changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

# Breaking Changes

## 6.x to 7.x
* The `EventFactoryInterface` has been changed, in case you are injecting your custom Event Factory, you will be affected.
Binary file added docs/examples/blob/span_overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/examples/blob/span_stacktrace.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/examples/metricset.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
];

$agent = new Agent($config);
$agent->putMetricset([
$agent->putEvent($agent->factory->newMetricset([
'system.cpu.total.norm.pct' => min(sys_getloadavg()[0]/100, 1),
]);
]));

// more Events to trace ..

Expand Down
111 changes: 80 additions & 31 deletions docs/examples/spans.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,79 @@
# Adding spans
Addings spans (https://www.elastic.co/guide/en/apm/server/current/transactions.html#transaction-spans) is easy.
Please consult the documentation for your exact needs.
Below is an example to add spans for MySQL, Redis and generic request wraped by a parent span.

Please consult the documentation for your exact needs. Below is an example for adding a MySQL span.
![Dashboard](https://github.com/philkra/elastic-apm-php-agent/blob/master/docs/examples/blob/span_dashboard.png "Spans Dashboard") ![Stacktrace](https://github.com/philkra/elastic-apm-php-agent/blob/master/docs/examples/blob/span_stacktrace.png "Span Stacktrace")

```php
$trxName = 'GET /some/transaction/name';

// create the agent
$agent = new \PhilKra\Agent(['appName' => 'Demo with Spans']);
$agent = new \PhilKra\Agent(['appName' => 'examples']);

$agent = new Agent($config);

// Span
// start a new transaction
$transaction = $agent->startTransaction($trxName);

// create a span
$spans = [];
$spans[] = [
'name' => 'Your Span Name. eg: ORM Query',
'type' => 'db.mysql.query',
'start' => 300, // when did tht query start, relative to the transaction start, in milliseconds
'duration' => 23, // duration, in milliseconds
'stacktrace' => [
$parent = $agent->startTransaction('POST /auth/examples/spans');

// burn some time
usleep(rand(10, 25));

// Create Span
$spanParent = $agent->factory()->newSpan('Authenication Workflow', $parent);
$spanParent->start();

// Lookup the User 'foobar' in the database
$spanDb = $agent->factory()->newSpan('DB User Lookup', $spanParent);
$spanDb->setType('db.mysql.query');
$spanDb->setAction('query');
$spanDb->start();

// do some db.mysql.query action ..
usleep(rand(100, 300));

$spanDb->stop();
// Attach addition/optional Context
$spanDb->setContext(['db' => [
'instance' => 'my_database', // the database name
'statement' => 'select * from non_existing_table where username = "foobar"', // the query being executed
'type' => 'sql',
'user' => 'user',
]]);
$agent->putEvent($spanDb);

// Stach the record into Redis
$spanCache = $agent->factory()->newSpan('DB User Lookup', $spanParent);
$spanCache->setType('db.redis.query');
$spanCache->setAction('query');
$spanCache->start();


// do some db.mysql.query action ..
usleep(rand(10, 30));

$spanCache->stop();
$spanCache->setContext(['db' => [
'instance' => 'redis01.example.foo',
'statement' => 'SET user_foobar "12345"',
]]);
$agent->putEvent($spanCache);

// Create another Span that is a parent span
$spanHash = $agent->factory()->newSpan('Validate Credentials', $spanParent);
$spanHash->start();

// do some password hashing and matching ..
usleep(rand(50, 100));

$spanHash->stop();
$agent->putEvent($spanHash);

// Create another Span that is a parent span
$spanSt = $agent->factory()->newSpan('Span with stacktrace', $spanParent);
$spanSt->start();

// burn some fictive time ..
usleep(rand(250, 350));
$spanSt->setStackTrace([
[
'function' => "\\YourOrMe\\Library\\Class::methodCall()",
'abs_path' => '/full/path/to/file.php',
Expand All @@ -41,25 +95,20 @@ $spans[] = [
'$table = $fakeTableBuilder->buildWithResult($result);',
'return $table;',
],
],
],
'context' => [
'db' => [
'instance' => 'my_database', // the database name
'statement' => 'select * from non_existing_table', // the query being executed
'type' => 'sql',
'user' => 'root', // the user executing the query (don't use root!)
],
],
];

// add the array of spans to the transaction
$transaction->setSpans($spans);
]
]);

$spanSt->stop();
$agent->putEvent($spanSt);

$spanParent->stop();

// Do some stuff you want to watch ...
sleep(1);
usleep(rand(100, 250));

$agent->putEvent($spanParent);

$agent->stopTransaction($trxName);
$agent->stopTransaction($parent->getTransactionName());

// send our transactions to the apm
$agent->send();
Expand Down
2 changes: 1 addition & 1 deletion docs/knowledgebase.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# Knowledgebase

# Disable Agent for CLI
## Disable Agent for CLI
In case you want to disable the agent dynamically for hybrid SAPI usage, please use the following snippet.
```php
'active' => PHP_SAPI !== 'cli'
Expand Down
38 changes: 19 additions & 19 deletions src/Agent.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
use PhilKra\Events\DefaultEventFactory;
use PhilKra\Events\EventFactoryInterface;
use PhilKra\Stores\TransactionsStore;
use PhilKra\Events\EventBean;
use PhilKra\Events\Error;
use PhilKra\Events\Transaction;
use PhilKra\Events\Metricset;
use PhilKra\Events\Metadata;
use PhilKra\Helper\Timer;
use PhilKra\Helper\Config;
Expand All @@ -29,7 +29,7 @@ class Agent
*
* @var string
*/
const VERSION = '7.0.0-beta2';
const VERSION = '7.0.0-beta3';

/**
* Agent Name
Expand Down Expand Up @@ -121,6 +121,16 @@ public function __construct(array $config, array $sharedContext = [], EventFacto
$this->timer->start();
}

/**
* Event Factory
*
* @return EventFactoryInterface
*/
public function factory() : EventFactoryInterface
{
return $this->eventFactory;
}

/**
* Query the Info endpoint of the APM Server
*
Expand All @@ -147,7 +157,7 @@ public function startTransaction(string $name, array $context = [], float $start
{
// Create and Store Transaction
$this->transactionsStore->register(
$this->eventFactory->createTransaction($name, array_replace_recursive($this->sharedContext, $context), $start)
$this->factory()->newTransaction($name, array_replace_recursive($this->sharedContext, $context), $start)
);

// Start the Transaction
Expand Down Expand Up @@ -209,25 +219,15 @@ public function getTransaction(string $name)
*/
public function captureThrowable(\Throwable $thrown, array $context = [], ?Transaction $parent = null)
{
$error = $this->eventFactory->createError($thrown, array_replace_recursive($this->sharedContext, $context), $parent);
if ($parent !== null) {
$parent->addError($error);
}
$this->connector->putEvent($error);
$this->putEvent($this->factory()->newError($thrown, array_replace_recursive($this->sharedContext, $context), $parent));
}

/**
* Register Metricset
*
* @link https://www.elastic.co/guide/en/apm/server/7.3/metricset-api.html
* @link https://github.com/elastic/apm-server/blob/master/docs/spec/metricsets/metricset.json
*
* @param array $set, k-v pair ['sys.avg.load' => 89]
* @param array $tags, Default []
* Put an Event to the Events Pool
*/
public function putMetricset(array $set, array $tags = [])
public function putEvent(EventBean $event)
{
$this->connector->putEvent(new Metricset($set, $tags));
$this->connector->putEvent($event);
}

/**
Expand Down Expand Up @@ -259,15 +259,15 @@ public function send() : bool
// Put the preceding Metadata
// TODO -- add context ?
if($this->connector->isPayloadSet() === false) {
$this->connector->putEvent(new Metadata([], $this->config));
$this->putEvent(new Metadata([], $this->config));
}

// Start Payload commitment
foreach($this->transactionsStore->list() as $event) {
$this->connector->putEvent($event);
}
$this->transactionsStore->reset();

return $this->connector->commit();
}

}
23 changes: 20 additions & 3 deletions src/Events/DefaultEventFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,33 @@ final class DefaultEventFactory implements EventFactoryInterface
/**
* {@inheritdoc}
*/
public function createError(\Throwable $throwable, array $contexts, ?Transaction $transaction = null): Error
public function newError(\Throwable $throwable, array $contexts, ?Transaction $parent = null): Error
{
return new Error($throwable, $contexts, $transaction);
return new Error($throwable, $contexts, $parent);
}

/**
* {@inheritdoc}
*/
public function createTransaction(string $name, array $contexts, float $start = null): Transaction
public function newTransaction(string $name, array $contexts, float $start = null): Transaction
{
return new Transaction($name, $contexts, $start);
}

/**
* {@inheritdoc}
*/
public function newSpan(string $name, EventBean $parent): Span
{
return new Span($name, $parent, $contexts, $start);
}

/**
* {@inheritdoc}
*/
public function newMetricset($set, $tags): Metricset
{
return new Metricset($set, $tags);
}

}
28 changes: 23 additions & 5 deletions src/Events/EventBean.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ class EventBean
*/
private $parentId = null;

/**
* Offset between the current Event and the parent Event's
*
* @var number
*/
private $parentTimestampOffset = null;

/**
* Error occurred on Timestamp
*
Expand Down Expand Up @@ -143,13 +150,23 @@ final public function setParentId(string $parentId)
/**
* Get the Parent Id
*
* @return string $parentId
* @return string
*/
final public function getParentId() : ?string
{
return $this->parentId;
}

/**
* Get the Offset between the Parent's timestamp and this Event's
*
* @return int
*/
final public function getParentTimestampOffset(): ?int
{
return $this->parentTimestampOffset;
}

/**
* Get the Event's Timestamp
*
Expand All @@ -161,16 +178,17 @@ public function getTimestamp() : int
}

/**
* Set the Parent Id and Trace Id
* Set the Parent Id and Trace Id based on the Parent Event
*
* @link https://www.elastic.co/guide/en/apm/server/current/transaction-api.html
*
* @param Transaction $parent
* @param EventBean $parent
*/
public function setParent(Transaction $parent)
public function setParent(EventBean $parent)
{
$this->parentId = $parent->getId();
$this->setParentId($parent->getId());
$this->setTraceId($parent->getTraceId());
$this->parentTimestampOffset = ($this->getTimestamp() - $parent->getTimestamp());
}

/**
Expand Down
Loading

0 comments on commit 09371df

Please sign in to comment.