ErrorReporting: No "serviceContext" on logs from Cloud Run
When following the Laravel instructions on https://cloud.google.com/error-reporting/docs/setup/php#framework_integrations for a Laravel Lumen service running on Cloud Run, the ErrorReporting library writes logs to Stackdriver. However it seems because it is not an App Engine Service, the necessary meta data is not included, which results in logs with "global" resource and missing service context labels:
{
"insertId": "1k2543ig364hmad",
"jsonPayload": {
"message": "…",
"serviceContext": {
"version": "", // Empty
"service": "" // Empty
},
"context": {
"reportLocation": {
"functionName": "…",
"filePath": "…",
"lineNumber": 53
}
}
},
"resource": {
"type": "global", // Not cloud_run_revision
"labels": {
"project_id": "…"
}
},
"timestamp": "…",
"severity": "ERROR",
"logName": "…",
"receiveTimestamp": "…"
}
This leads to the errors not being picked up by Error Reporting which means that no notifications for new Errors are sent.
What I found is that when a new PsrLogger is instantiated, no metadataProvider is passed. This leads to the default one being used, which seems to be only handling cases for App Engine services:
// vendor/google/cloud-core/src/Report/MetadataProviderUtils.php
public static function autoSelect($server)
{
if (isset($server['GAE_SERVICE'])) {
if (isset($server['GAE_ENV']) && $server['GAE_ENV'] === 'standard') {
return new GAEStandardMetadataProvider($server);
}
return new GAEFlexMetadataProvider($server);
}
return new EmptyMetadataProvider();
}
Is this intentional? If so, shouldn't the library set the correct meta data when run on Cloud Run so that ErrorReporting recognizes the exception?
Steps to reproduce
- Setup Laravel or similar framework on Cloud Run and catch exceptions:
public function report(Exception $exception)
{
// Ensure Stackdriver is initialized and handle the exception
Bootstrap::init();
Bootstrap::exceptionHandler($exception);
parent::report($exception);
}
- Throw exception.
- Check Logs in GC Project, find exception and check for missing service labels.
Environment details
- OS: macOS 10.14.6
- PHP version: 7.3.14
- Package name and version:
google/cloud-error-reporting0.16.1
@jdpedrie Hey, do you have any update on this?
Sorry @niklasravnsborg I don't work on this project anymore. I'm sure someone will weigh in soon though!
Hi @niklasravnsborg! Congratulations, you are the owner of our longest open issue!
I am sorry it's taken so long for someone to take a look at this. Looking at the code, the class should be getting back the expected values from the metadata server, which is available on Cloud Run, App Engine, OR GCE. If you're on any Google platform, those values should be getting populated. This would work if the logger was using the Component/Metadata class, but it isn't because it's calling this class instead, which returns an empty metadata provider.
This should be an easy fix, let me see what i can do.
Okay looking at this more (and thinking about this more), the Bootstrap class calls serviceId and versionId, which do not exist in the Cloud Run environment in the same way as they do in the App Engine environment.
Therefore, if these are values you want populated, I'd say the best approach (right now) would be to use the Google\Cloud\Core\Report\SimpleMetadataProvider class, pass it into an instance of Google\Cloud\Logging\PsrLogger class which would then return your own MetadataProvider class, which could provide the values for serviceId and versionId that you'd like to see.
use Google\Cloud\Core\Report\SimpleMetadataProvider;
use Google\Cloud\Logging\PsrLogger;
use Google\Cloud\ErrorReporting\Bootstrap;
$myServiceId = 'YourServiceID';
$myVersionId = 'YourVersionID';
$metadataProvider = new SimpleMetadataProvider(serviceId: $myServiceId, versionId: $myVersionId);
$psrLogger = (new LoggingClient())
->psrLogger(self::DEFAULT_LOGNAME, [
'metadataProvider' => $metadataProvider
'batchEnabled' => true,
'debugOutput' => true,
'batchOptions' => [
'numWorkers' => 2
]
]);
Bootstrap::init($psrLogger);
In the meantime, there's still work we could do to automatically detect Cloud Run and provide the service. I do not think Cloud Run has the idea of a "version". @glasnt (who wrote the amazing Cloud Run for Laravel Tutorial) may know more.
Looks like we can use the K_SERVICE and K_REVISION environment variables in the Cloud Run runtime contract to get this information:
$metadataProvider = new SimpleMetadataProvider(serviceId: getenv('K_SERVICE'), versionId: getenv('K_REVISION'));
I've submitted a PR to add them automatically: https://github.com/googleapis/google-cloud-php/pull/5980