feat(sites): add screenshot success telemetry to usage stats

This commit is contained in:
harsh mahajan
2026-04-15 11:43:53 +05:30
parent 445e86361c
commit db0a971384
7 changed files with 169 additions and 15 deletions
+6
View File
@@ -351,6 +351,8 @@ const METRIC_BUILDS_STORAGE = 'builds.storage';
const METRIC_BUILDS_COMPUTE = 'builds.compute';
const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success';
const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed';
const METRIC_SCREENSHOTS_SUCCESS = 'screenshots.success';
const METRIC_SCREENSHOTS_FAILED = 'screenshots.failed';
const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds';
const METRIC_EXECUTIONS = 'executions';
const METRIC_EXECUTIONS_COMPUTE = 'executions.compute';
@@ -363,6 +365,8 @@ const METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED = '{resourceType}.{resourceInternal
const METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE = '{resourceType}.{resourceInternalId}.builds.compute';
const METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE_SUCCESS = '{resourceType}.{resourceInternalId}.builds.compute.success';
const METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE_FAILED = '{resourceType}.{resourceInternalId}.builds.compute.failed';
const METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_SUCCESS = '{resourceType}.{resourceInternalId}.screenshots.success';
const METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_FAILED = '{resourceType}.{resourceInternalId}.screenshots.failed';
const METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS = '{resourceType}.{resourceInternalId}.builds.mbSeconds';
const METRIC_RESOURCE_TYPE_ID_BUILDS = '{resourceType}.{resourceInternalId}.builds';
const METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE = '{resourceType}.{resourceInternalId}.builds.storage';
@@ -376,6 +380,8 @@ const METRIC_RESOURCE_TYPE_BUILDS_FAILED = '{resourceType}.builds.failed';
const METRIC_RESOURCE_TYPE_BUILDS_COMPUTE = '{resourceType}.builds.compute';
const METRIC_RESOURCE_TYPE_BUILDS_COMPUTE_SUCCESS = '{resourceType}.builds.compute.success';
const METRIC_RESOURCE_TYPE_BUILDS_COMPUTE_FAILED = '{resourceType}.builds.compute.failed';
const METRIC_RESOURCE_TYPE_SCREENSHOTS_SUCCESS = '{resourceType}.screenshots.success';
const METRIC_RESOURCE_TYPE_SCREENSHOTS_FAILED = '{resourceType}.screenshots.failed';
const METRIC_RESOURCE_TYPE_BUILDS_MB_SECONDS = '{resourceType}.builds.mbSeconds';
const METRIC_RESOURCE_TYPE_BUILDS = '{resourceType}.builds';
const METRIC_RESOURCE_TYPE_BUILDS_STORAGE = '{resourceType}.builds.storage';
@@ -3,9 +3,12 @@
namespace Appwrite\Platform\Modules\Functions\Workers;
use Ahc\Jwt\JWT;
use Appwrite\Event\Message\Usage as UsageMessage;
use Appwrite\Event\Publisher\Usage as UsagePublisher;
use Appwrite\Event\Realtime;
use Appwrite\Permission;
use Appwrite\Role;
use Appwrite\Usage\Context;
use Exception;
use Utopia\Compression\Compression;
use Utopia\Config\Config;
@@ -43,6 +46,7 @@ class Screenshots extends Action
->inject('dbForProject')
->inject('project')
->inject('deviceForFiles')
->inject('publisherForUsage')
->callback($this->action(...));
}
@@ -52,7 +56,8 @@ class Screenshots extends Action
Database $dbForPlatform,
Database $dbForProject,
Document $project,
Device $deviceForFiles
Device $deviceForFiles,
UsagePublisher $publisherForUsage
): void {
Console::log('Screenshot action started');
@@ -257,6 +262,13 @@ class Screenshots extends Action
'deploymentScreenshotDark' => $deployment->getAttribute('screenshotDark', ''),
'deploymentScreenshotLight' => $deployment->getAttribute('screenshotLight', ''),
]));
$this->publishUsage(
project: $project,
site: $site,
publisherForUsage: $publisherForUsage,
metric: METRIC_SCREENSHOTS_SUCCESS
);
} catch (\Throwable $th) {
Console::warning("Screenshot failed to generate:");
Console::warning($th->getMessage());
@@ -265,10 +277,51 @@ class Screenshots extends Action
$date = \date('H:i:s');
$this->appendToLogs($dbForProject, $deployment->getId(), $queueForRealtime, "[$date] [appwrite] Screenshot capturing failed. Deployment will continue. \n");
$this->publishUsage(
project: $project,
site: $site,
publisherForUsage: $publisherForUsage,
metric: METRIC_SCREENSHOTS_FAILED
);
throw $th;
}
}
protected function publishUsage(
Document $project,
Document $site,
UsagePublisher $publisherForUsage,
string $metric
): void {
[$resourceMetric, $resourceIdMetric] = match ($metric) {
METRIC_SCREENSHOTS_SUCCESS => [
METRIC_RESOURCE_TYPE_SCREENSHOTS_SUCCESS,
METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_SUCCESS,
],
METRIC_SCREENSHOTS_FAILED => [
METRIC_RESOURCE_TYPE_SCREENSHOTS_FAILED,
METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_FAILED,
],
default => throw new \InvalidArgumentException('Unknown screenshot metric: ' . $metric),
};
$usage = (new Context())
->addMetric($metric, 1)
->addMetric(str_replace('{resourceType}', RESOURCE_TYPE_SITES, $resourceMetric), 1)
->addMetric(str_replace(
['{resourceType}', '{resourceInternalId}'],
[RESOURCE_TYPE_SITES, $site->getSequence()],
$resourceIdMetric
), 1);
$publisherForUsage->enqueue(new UsageMessage(
project: $project,
metrics: $usage->getMetrics(),
reduce: $usage->getReduce()
));
}
protected function appendToLogs(Database $dbForProject, string $deploymentId, Realtime $queueForRealtime, string $logs)
{
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
@@ -87,6 +87,8 @@ class Get extends Base
str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS),
str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_SUCCESS),
str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED),
str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_SUCCESS),
str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_FAILED),
str_replace(['{siteInternalId}'], [$site->getSequence()], METRIC_SITES_ID_REQUESTS),
str_replace(['{siteInternalId}'], [$site->getSequence()], METRIC_SITES_ID_INBOUND),
str_replace(['{siteInternalId}'], [$site->getSequence()], METRIC_SITES_ID_OUTBOUND),
@@ -139,6 +141,9 @@ class Get extends Base
$buildsTimeTotal = $usage[$metrics[4]]['total'] ?? 0;
$buildsTotal = $usage[$metrics[2]]['total'] ?? 0;
$screenshotsSuccessTotal = $usage[$metrics[11]]['total'] ?? 0;
$screenshotsFailedTotal = $usage[$metrics[12]]['total'] ?? 0;
$screenshotsTotal = $screenshotsSuccessTotal + $screenshotsFailedTotal;
$response->dynamic(new Document([
'range' => $range,
'deploymentsTotal' => $usage[$metrics[0]]['total'],
@@ -153,9 +158,12 @@ class Get extends Base
'executionsMbSecondsTotal' => $usage[$metrics[8]]['total'],
'buildsSuccessTotal' => $usage[$metrics[9]]['total'],
'buildsFailedTotal' => $usage[$metrics[10]]['total'],
'requestsTotal' => $usage[$metrics[11]]['total'],
'inboundTotal' => $usage[$metrics[12]]['total'],
'outboundTotal' => $usage[$metrics[13]]['total'],
'screenshotsSuccessTotal' => $screenshotsSuccessTotal,
'screenshotsFailedTotal' => $screenshotsFailedTotal,
'screenshotsSuccessRate' => $screenshotsTotal === 0 ? 0 : $screenshotsSuccessTotal / $screenshotsTotal,
'requestsTotal' => $usage[$metrics[13]]['total'],
'inboundTotal' => $usage[$metrics[14]]['total'],
'outboundTotal' => $usage[$metrics[15]]['total'],
'deployments' => $usage[$metrics[0]]['data'],
'deploymentsStorage' => $usage[$metrics[1]]['data'],
'builds' => $usage[$metrics[2]]['data'],
@@ -167,9 +175,11 @@ class Get extends Base
'executionsMbSeconds' => $usage[$metrics[8]]['data'],
'buildsSuccess' => $usage[$metrics[9]]['data'],
'buildsFailed' => $usage[$metrics[10]]['data'],
'requests' => $usage[$metrics[11]]['data'],
'inbound' => $usage[$metrics[12]]['data'],
'outbound' => $usage[$metrics[13]]['data'],
'screenshotsSuccess' => $usage[$metrics[11]]['data'],
'screenshotsFailed' => $usage[$metrics[12]]['data'],
'requests' => $usage[$metrics[13]]['data'],
'inbound' => $usage[$metrics[14]]['data'],
'outbound' => $usage[$metrics[15]]['data'],
]), Response::MODEL_USAGE_SITE);
}
}
@@ -74,6 +74,8 @@ class XList extends Base
str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_EXECUTIONS_MB_SECONDS),
str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_BUILDS_SUCCESS),
str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_BUILDS_FAILED),
str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_SCREENSHOTS_SUCCESS),
str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_SCREENSHOTS_FAILED),
METRIC_SITES_REQUESTS,
METRIC_SITES_INBOUND,
METRIC_SITES_OUTBOUND,
@@ -122,6 +124,10 @@ class XList extends Base
];
}
}
$screenshotsSuccessTotal = $usage[$metrics[12]]['total'] ?? 0;
$screenshotsFailedTotal = $usage[$metrics[13]]['total'] ?? 0;
$screenshotsTotal = $screenshotsSuccessTotal + $screenshotsFailedTotal;
$response->dynamic(new Document([
'range' => $range,
'sitesTotal' => $usage[$metrics[0]]['total'],
@@ -136,9 +142,12 @@ class XList extends Base
'executionsMbSecondsTotal' => $usage[$metrics[9]]['total'],
'buildsSuccessTotal' => $usage[$metrics[10]]['total'],
'buildsFailedTotal' => $usage[$metrics[11]]['total'],
'requestsTotal' => $usage[$metrics[12]]['total'],
'inboundTotal' => $usage[$metrics[13]]['total'],
'outboundTotal' => $usage[$metrics[14]]['total'],
'screenshotsSuccessTotal' => $screenshotsSuccessTotal,
'screenshotsFailedTotal' => $screenshotsFailedTotal,
'screenshotsSuccessRate' => $screenshotsTotal === 0 ? 0 : $screenshotsSuccessTotal / $screenshotsTotal,
'requestsTotal' => $usage[$metrics[14]]['total'],
'inboundTotal' => $usage[$metrics[15]]['total'],
'outboundTotal' => $usage[$metrics[16]]['total'],
'sites' => $usage[$metrics[0]]['data'],
'deployments' => $usage[$metrics[1]]['data'],
'deploymentsStorage' => $usage[$metrics[2]]['data'],
@@ -151,9 +160,11 @@ class XList extends Base
'executionsMbSeconds' => $usage[$metrics[9]]['data'],
'buildsSuccess' => $usage[$metrics[10]]['data'],
'buildsFailed' => $usage[$metrics[11]]['data'],
'requests' => $usage[$metrics[12]]['data'],
'inbound' => $usage[$metrics[13]]['data'],
'outbound' => $usage[$metrics[14]]['data'],
'screenshotsSuccess' => $usage[$metrics[12]]['data'],
'screenshotsFailed' => $usage[$metrics[13]]['data'],
'requests' => $usage[$metrics[14]]['data'],
'inbound' => $usage[$metrics[15]]['data'],
'outbound' => $usage[$metrics[16]]['data'],
]), Response::MODEL_USAGE_SITES);
}
}
@@ -49,6 +49,38 @@ class UsageSite extends UsageFunction
'example' => [],
'array' => true
])
->addRule('screenshotsSuccessTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total aggregated number of successful site screenshots.',
'default' => 0,
'example' => 0,
])
->addRule('screenshotsFailedTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total aggregated number of failed site screenshots.',
'default' => 0,
'example' => 0,
])
->addRule('screenshotsSuccessRate', [
'type' => self::TYPE_FLOAT,
'description' => 'Success rate of site screenshots from 0 to 1.',
'default' => 0,
'example' => 0,
])
->addRule('screenshotsSuccess', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated number of successful site screenshots per period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('screenshotsFailed', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated number of failed site screenshots per period.',
'default' => [],
'example' => [],
'array' => true
])
;
}
@@ -122,6 +122,38 @@ class UsageSites extends Model
'example' => [],
'array' => true
])
->addRule('screenshotsSuccessTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total aggregated number of successful site screenshots.',
'default' => 0,
'example' => 0,
])
->addRule('screenshotsFailedTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total aggregated number of failed site screenshots.',
'default' => 0,
'example' => 0,
])
->addRule('screenshotsSuccessRate', [
'type' => self::TYPE_FLOAT,
'description' => 'Success rate of site screenshots from 0 to 1.',
'default' => 0,
'example' => 0,
])
->addRule('screenshotsSuccess', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated number of successful site screenshots per period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('screenshotsFailed', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated number of failed site screenshots per period.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('deployments', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated number of sites deployment per period.',
+12 -2
View File
@@ -1675,11 +1675,14 @@ class UsageTest extends Scope
);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(30, count($response['body']));
$this->assertEquals(35, count($response['body']));
$this->assertEquals('30d', $response['body']['range']);
$this->assertIsArray($response['body']['deployments']);
$this->assertEquals($deploymentsSuccess, $response['body']['buildsSuccessTotal']);
$this->assertEquals($deploymentsFailed, $response['body']['buildsFailedTotal']);
$this->assertGreaterThanOrEqual(1, $response['body']['screenshotsSuccessTotal']);
$this->assertEquals(0, $response['body']['screenshotsFailedTotal']);
$this->assertEquals(1.0, $response['body']['screenshotsSuccessRate']);
$this->assertIsArray($response['body']['deploymentsStorage']);
$this->assertIsNumeric($response['body']['deploymentsStorageTotal']);
$this->assertIsNumeric($response['body']['buildsMbSecondsTotal']);
@@ -1692,6 +1695,8 @@ class UsageTest extends Scope
$this->assertIsArray($response['body']['executionsMbSeconds']);
$this->assertIsArray($response['body']['buildsSuccess']);
$this->assertIsArray($response['body']['buildsFailed']);
$this->assertIsArray($response['body']['screenshotsSuccess']);
$this->assertIsArray($response['body']['screenshotsFailed']);
$this->assertIsArray($response['body']['requests']);
$this->assertIsArray($response['body']['inbound']);
$this->assertIsArray($response['body']['outbound']);
@@ -1709,7 +1714,7 @@ class UsageTest extends Scope
);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(31, count($response['body']));
$this->assertEquals(36, count($response['body']));
$this->assertEquals($response['body']['range'], '30d');
$this->assertIsArray($response['body']['sites']);
$this->assertIsArray($response['body']['deployments']);
@@ -1722,6 +1727,11 @@ class UsageTest extends Scope
$this->assertIsArray($response['body']['executionsMbSeconds']);
$this->assertIsArray($response['body']['buildsSuccess']);
$this->assertIsArray($response['body']['buildsFailed']);
$this->assertGreaterThanOrEqual(1, $response['body']['screenshotsSuccessTotal']);
$this->assertEquals(0, $response['body']['screenshotsFailedTotal']);
$this->assertEquals(1.0, $response['body']['screenshotsSuccessRate']);
$this->assertIsArray($response['body']['screenshotsSuccess']);
$this->assertIsArray($response['body']['screenshotsFailed']);
$this->assertIsArray($response['body']['requests']);
$this->assertIsArray($response['body']['inbound']);
$this->assertIsArray($response['body']['outbound']);