mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Added function usage tracking
This commit is contained in:
@@ -112,6 +112,104 @@ App::get('/v1/functions/:functionId')
|
||||
$response->json($function->getArrayCopy());
|
||||
}, ['response', 'projectDB']);
|
||||
|
||||
App::get('/v1/functions/:functionId/usage')
|
||||
->desc('Get Function Usage')
|
||||
->groups(['api', 'functions'])
|
||||
->label('scope', 'functions.read')
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'getUsage')
|
||||
->param('functionId', '', function () { return new UID(); }, 'Function unique ID.')
|
||||
->param('range', 'last30', function () { return new WhiteList(['daily', 'monthly', 'last30', 'last90']); }, 'Date range.', true)
|
||||
->action(function ($functionId, $range, $response, $project, $projectDB, $register) {
|
||||
/** @var Utopia\Response $response */
|
||||
/** @var Appwrite\Database\Document $project */
|
||||
/** @var Appwrite\Database\Database $consoleDB */
|
||||
/** @var Appwrite\Database\Database $projectDB */
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
|
||||
$function = $projectDB->getDocument($functionId);
|
||||
|
||||
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
|
||||
throw new Exception('function not found', 404);
|
||||
}
|
||||
|
||||
$period = [
|
||||
'daily' => [
|
||||
'start' => DateTime::createFromFormat('U', \strtotime('today')),
|
||||
'end' => DateTime::createFromFormat('U', \strtotime('tomorrow')),
|
||||
'group' => '1m',
|
||||
],
|
||||
'monthly' => [
|
||||
'start' => DateTime::createFromFormat('U', \strtotime('midnight first day of this month')),
|
||||
'end' => DateTime::createFromFormat('U', \strtotime('midnight last day of this month')),
|
||||
'group' => '1d',
|
||||
],
|
||||
'last30' => [
|
||||
'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
|
||||
'end' => DateTime::createFromFormat('U', \strtotime('tomorrow')),
|
||||
'group' => '1d',
|
||||
],
|
||||
'last90' => [
|
||||
'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
|
||||
'end' => DateTime::createFromFormat('U', \strtotime('today')),
|
||||
'group' => '1d',
|
||||
],
|
||||
// 'yearly' => [
|
||||
// 'start' => DateTime::createFromFormat('U', strtotime('midnight first day of january')),
|
||||
// 'end' => DateTime::createFromFormat('U', strtotime('midnight last day of december')),
|
||||
// 'group' => '4w',
|
||||
// ],
|
||||
];
|
||||
|
||||
$client = $register->get('influxdb');
|
||||
|
||||
$executions = [];
|
||||
$compute = [];
|
||||
|
||||
if ($client) {
|
||||
$start = $period[$range]['start']->format(DateTime::RFC3339);
|
||||
$end = $period[$range]['end']->format(DateTime::RFC3339);
|
||||
$database = $client->selectDB('telegraf');
|
||||
|
||||
// Executions
|
||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
|
||||
$points = $result->getPoints();
|
||||
|
||||
foreach ($points as $point) {
|
||||
$executions[] = [
|
||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
||||
'date' => \strtotime($point['time']),
|
||||
];
|
||||
}
|
||||
|
||||
// Compute
|
||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_time" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
|
||||
$points = $result->getPoints();
|
||||
|
||||
foreach ($points as $point) {
|
||||
$compute[] = [
|
||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
||||
'date' => \strtotime($point['time']),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$response->json([
|
||||
'executions' => [
|
||||
'data' => $executions,
|
||||
'total' => \array_sum(\array_map(function ($item) {
|
||||
return $item['value'];
|
||||
}, $executions)),
|
||||
],
|
||||
'compute' => [
|
||||
'data' => $compute,
|
||||
'total' => \array_sum(\array_map(function ($item) {
|
||||
return $item['value'];
|
||||
}, $compute)),
|
||||
],
|
||||
]);
|
||||
}, ['response', 'consoleDB', 'projectDB', 'register']);
|
||||
|
||||
App::put('/v1/functions/:functionId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Update Function')
|
||||
|
||||
@@ -206,6 +206,7 @@ App::get('/v1/projects/:projectId/usage')
|
||||
|
||||
$requests = [];
|
||||
$network = [];
|
||||
$functions = [];
|
||||
|
||||
if ($client) {
|
||||
$start = $period[$range]['start']->format(DateTime::RFC3339);
|
||||
@@ -233,6 +234,17 @@ App::get('/v1/projects/:projectId/usage')
|
||||
'date' => \strtotime($point['time']),
|
||||
];
|
||||
}
|
||||
|
||||
// Functions
|
||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
|
||||
$points = $result->getPoints();
|
||||
|
||||
foreach ($points as $point) {
|
||||
$functions[] = [
|
||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
||||
'date' => \strtotime($point['time']),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Users
|
||||
@@ -289,6 +301,12 @@ App::get('/v1/projects/:projectId/usage')
|
||||
return $item['value'];
|
||||
}, $network)),
|
||||
],
|
||||
'functions' => [
|
||||
'data' => $functions,
|
||||
'total' => \array_sum(\array_map(function ($item) {
|
||||
return $item['value'];
|
||||
}, $functions)),
|
||||
],
|
||||
'collections' => [
|
||||
'data' => $collections,
|
||||
'total' => $collectionsTotal,
|
||||
|
||||
@@ -277,8 +277,8 @@ $timeout = $this->getParam('timeout', 900);
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="30"></th>
|
||||
<th width="170">Triggered</th>
|
||||
<th width="150">Status</th>
|
||||
<th width="170">Date</th>
|
||||
<th width="100">Runtime</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
@@ -291,13 +291,13 @@ $timeout = $this->getParam('timeout', 900);
|
||||
<i class="dot info" data-ls-if="{{execution.status}} === 'processing'"></i>
|
||||
<i class="dot success" data-ls-if="{{execution.status}} === 'completed'"></i>
|
||||
</td>
|
||||
<td data-title="Date: ">
|
||||
<span data-ls-bind="{{execution.dateCreated|dateTime}}"></span>
|
||||
</td>
|
||||
<td data-title="Status: ">
|
||||
<span data-ls-bind="{{execution.status}}"></span>
|
||||
<span class="text-fade text-size-small" data-ls-if="{{execution.exitCode}} !== 0" data-ls-bind=" exit code: {{execution.exitCode}}"></span>
|
||||
</td>
|
||||
<td data-title="Date: ">
|
||||
<span data-ls-bind="{{execution.dateCreated|dateTime}}"></span>
|
||||
</td>
|
||||
<td data-title="Runtime: ">
|
||||
<span data-ls-if="{{execution.status}} === 'completed' || {{execution.status}} === 'failed'" data-ls-bind="{{execution.time|seconds2hum}}"></span>
|
||||
<span data-ls-if="{{execution.status}} === 'waiting' || {{execution.status}} === 'processing'">-</span>
|
||||
|
||||
@@ -23,7 +23,7 @@ $graph = $this->getParam('graph', false);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone xl margin-top-negative-xxl">
|
||||
<div class="zone xxl margin-top-negative-xxl">
|
||||
<div class="box margin-bottom dashboard">
|
||||
<div
|
||||
data-service="projects.getUsage"
|
||||
@@ -52,6 +52,8 @@ $graph = $this->getParam('graph', false);
|
||||
<!-- <div class="margin-top dev-feature">
|
||||
<a href="">Full Usage Report <i class="icon-right-open"></i></a>
|
||||
</div> -->
|
||||
|
||||
<div class="margin-top-large"><b class="text-size-small unit" data-ls-bind="{{usage.functions.total|statsTotal}} Func. Executed" data-default="0 Func. Executed"></b></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -77,6 +79,14 @@ $graph = $this->getParam('graph', false);
|
||||
<div class="margin-top-small"><b class="text-size-small unit">Tasks</b></div>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
<hr />
|
||||
|
||||
<div class="row responsive">
|
||||
<div class="col span-4">x</div>
|
||||
<div class="col span-4">x</div>
|
||||
<div class="col span-4">x</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -302,6 +302,7 @@ class FunctionsV1
|
||||
, null, $stdout, $stderr, $function->getAttribute('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)));
|
||||
|
||||
$executionEnd = \microtime(true);
|
||||
$executionTime = ($executionEnd - $executionStart);
|
||||
|
||||
Console::info("Function executed in " . ($executionEnd - $executionStart) . " seconds with exit code {$exitCode}");
|
||||
|
||||
@@ -313,7 +314,7 @@ class FunctionsV1
|
||||
'exitCode' => $exitCode,
|
||||
'stdout' => mb_substr($stdout, -4000), // log last 4000 chars output
|
||||
'stderr' => mb_substr($stderr, -4000), // log last 4000 chars output
|
||||
'time' => ($executionEnd - $executionStart),
|
||||
'time' => $executionTime,
|
||||
]));
|
||||
|
||||
Authorization::reset();
|
||||
@@ -322,6 +323,19 @@ class FunctionsV1
|
||||
throw new Exception('Failed saving execution to DB', 500);
|
||||
}
|
||||
|
||||
$usage = $register->get('queue-usage');
|
||||
|
||||
$usage
|
||||
->setParam('projectId', $projectId)
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('functionExecution', 1)
|
||||
->setParam('functionExecutionTime', $executionTime) // Seconds
|
||||
->setParam('networkRequestSize', 0)
|
||||
->setParam('networkResponseSize', 0)
|
||||
;
|
||||
|
||||
$usage->trigger();
|
||||
|
||||
Console::success(count($list).' running containers counted');
|
||||
|
||||
$max = (int) App::getEnv('_APP_FUNCTIONS_CONTAINERS');
|
||||
|
||||
+11
-7
@@ -27,17 +27,18 @@ class UsageV1
|
||||
$statsd = $register->get('statsd', true);
|
||||
|
||||
$projectId = $this->args['projectId'];
|
||||
$httpMethod = $this->args['httpMethod'];
|
||||
$httpRequest = $this->args['httpRequest'];
|
||||
|
||||
|
||||
$storage = $this->args['storage'];
|
||||
|
||||
$networkRequestSize = $this->args['networkRequestSize'];
|
||||
$networkResponseSize = $this->args['networkResponseSize'];
|
||||
|
||||
$storage = $this->args['storage'];
|
||||
$httpMethod = $this->args['httpMethod'];
|
||||
$httpRequest = $this->args['httpRequest'];
|
||||
|
||||
$functionId = $this->args['functionId'];
|
||||
$functionExecution = $this->args['functionExecution'];
|
||||
$functionExecutionTime = $this->args['functionExecutionTime'];
|
||||
$functionId = $this->args['functionId'];
|
||||
|
||||
$tags = ",project={$projectId},version=".App::getEnv('_APP_VERSION', 'UNKNOWN').'';
|
||||
|
||||
@@ -53,10 +54,13 @@ class UsageV1
|
||||
$statsd->count('executions.time'.$tags.',functionId='.$functionId, $functionExecutionTime);
|
||||
}
|
||||
|
||||
$statsd->count('network.all'.$tags, $networkRequestSize + $networkResponseSize);
|
||||
$statsd->count('network.inbound'.$tags, $networkRequestSize);
|
||||
$statsd->count('network.outbound'.$tags, $networkResponseSize);
|
||||
$statsd->count('storage.all'.$tags, $storage);
|
||||
$statsd->count('network.all'.$tags, $networkRequestSize + $networkResponseSize);
|
||||
|
||||
if($storage >= 1) {
|
||||
$statsd->count('storage.all'.$tags, $storage);
|
||||
}
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -422,7 +422,7 @@
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 20px -20px;
|
||||
margin: 20px -25px;
|
||||
height: 2px;
|
||||
background: var(--config-console-background);
|
||||
|
||||
@@ -452,7 +452,6 @@
|
||||
display: block;
|
||||
width: 2px;
|
||||
background: var(--config-console-background);
|
||||
height: ~"calc(100% + 110px)";
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
bottom: -20px;
|
||||
|
||||
Reference in New Issue
Block a user