Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Cloud Functions Error Reporting sample #140

Merged
merged 6 commits into from
Jun 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion functions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ environment.
* [Cloud Datastore](datastore/)
* [Cloud Pub/Sub](pubsub/)
* [Dependencies](uuid/)
* [Error Reporting](errorreporting/)
* [HTTP](http/)
* [Logging](log/)
* [Logging & Monitoring](log/)
* [Modules](module/)
* [OCR (Optical Character Recognition)](ocr/)
* [SendGrid](sendgrid/)
141 changes: 141 additions & 0 deletions functions/errorreporting/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2016, Google, 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.

'use strict';

// [START setup]
var gcloud = require('gcloud');

// Get a reference to the StackDriver Logging component
var logging = gcloud.logging();
// [END setup]

// [START reportDetailedError]
var reportDetailedError = require('./report');
// [END reportDetailedError]

// [START helloSimpleErrorReport]
/**
* Report an error to StackDriver Error Reporting. Writes the minimum data
* required for the error to be picked up by StackDriver Error Reporting.
*
* @param {Error} err The Error object to report.
* @param {Function} callback Callback function.
*/
function reportError (err, callback) {
// This is the name of the StackDriver log stream that will receive the log
// entry. This name can be any valid log stream name, but must contain "err"
// in order for the error to be picked up by StackDriver Error Reporting.
var logName = 'errors';
var log = logging.log(logName);

// https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
var monitoredResource = {
type: 'cloud_function',
labels: {
function_name: process.env.FUNCTION_NAME
}
};

// https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent
var errorEvent = {
message: err.stack,
serviceContext: {
service: 'cloud_function:' + process.env.FUNCTION_NAME,
version: require('./package.json').version || 'unknown'
}
};

// Write the error log entry
log.write(log.entry(monitoredResource, errorEvent), callback);
}
// [END helloSimpleErrorReport]

// [START helloSimpleError]
/**
* HTTP Cloud Function.
*
* @param {Object} req Cloud Function request object.
* @param {Object} res Cloud Function response object.
*/
exports.helloSimpleError = function helloSimpleError (req, res) {
try {
if (req.method !== 'GET') {
var error = new Error('Only GET requests are accepted!');
error.code = 405;
throw error;
}
// All is good, respond to the HTTP request
return res.send('Hello World!');
} catch (err) {
// Report the error
return reportError(err, function () {
// Now respond to the HTTP request
return res.status(error.code || 500).send(err.message);
});
}
};
// [END helloSimpleError]

// [START helloHttpError]
/**
* HTTP Cloud Function.
*
* @param {Object} req Cloud Function request object.
* @param {Object} res Cloud Function response object.
*/
exports.helloHttpError = function helloHttpError (req, res) {
try {
if (req.method !== 'POST' && req.method !== 'GET') {
var error = new Error('Only POST and GET requests are accepted!');
error.code = 405;
throw error;
}
// All is good, respond to the HTTP request
return res.send('Hello ' + (req.body.message || 'World') + '!');
} catch (err) {
// Set the response status code before reporting the error
res.status(err.code || 500);
// Report the error
return reportDetailedError(err, req, res, function () {
// Now respond to the HTTP request
return res.send(err.message);
});
}
};
// [END helloHttpError]

// [START helloBackgroundError]
/**
* Background Cloud Function.
*
* @param {Object} context Cloud Function context object.
* @param {Object} data Request data, provided by a trigger.
* @param {string} data.message Message, provided by the trigger.
*/
exports.helloBackgroundError = function helloBackgroundError (context, data) {
try {
if (!data.message) {
throw new Error('"message" is required!');
}
// All is good, respond with a message
return context.success('Hello World!');
} catch (err) {
// Report the error
return reportDetailedError(err, function () {
// Now finish mark the execution failure
return context.failure(err.message);
});
}
};
// [END helloBackgroundError]
15 changes: 15 additions & 0 deletions functions/errorreporting/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "nodejs-docs-samples-functions-errorreporting",
"description": "Node.js samples found on https://cloud.google.com",
"version": "0.0.1",
"private": true,
"license": "Apache Version 2.0",
"author": "Google Inc.",
"repository": {
"type": "git",
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
},
"dependencies": {
"gcloud": "^0.36.0"
}
}
126 changes: 126 additions & 0 deletions functions/errorreporting/report.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright 2016, Google, 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.

'use strict';

var gcloud = require('gcloud');
var logging = gcloud.logging();

// [START helloHttpError]
/**
* Report an error to StackDriver Error Reporting. Writes up to the maximum data
* accepted by StackDriver Error Reporting.
*
* @param {Error} err The Error object to report.
* @param {Object} [req] Request context, if any.
* @param {Object} [res] Response context, if any.
* @param {Object} [options] Additional context, if any.
* @param {Function} callback Callback function.
*/
function reportDetailedError (err, req, res, options, callback) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this perhaps be upstreamed into gcloud?

if (typeof req === 'function') {
callback = req;
req = null;
res = null;
options = {};
} else if (typeof options === 'function') {
callback = options;
options = {};
}
options || (options = {});

var FUNCTION_NAME = process.env.FUNCTION_NAME;
var log = logging.log('errors');

// MonitoredResource
// See https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
var resource = {
// MonitoredResource.type
type: 'cloud_function',
// MonitoredResource.labels
labels: {
function_name: FUNCTION_NAME
}
};
if (typeof options.region === 'string') {
resource.labels.region = options.region;
}
if (typeof options.projectId === 'string') {
resource.labels.projectId = options.projectId;
}

var context = {};
if (typeof options.user === 'string') {
// ErrorEvent.context.user
context.user = options.user;
}
if (req && res) {
// ErrorEvent.context.httpRequest
context.httpRequest = {
method: req.method,
url: req.originalUrl,
userAgent: typeof req.get === 'function' ? req.get('user-agent') : 'unknown',
referrer: '',
remoteIp: req.ip
};
if (typeof res.statusCode === 'number') {
context.httpRequest.responseStatusCode = res.statusCode;
}
}
if (!(err instanceof Error) || typeof err.stack !== 'string') {
// ErrorEvent.context.reportLocation
context.reportLocation = {
filePath: typeof options.filePath === 'string' ? options.filePath : 'unknown',
lineNumber: typeof options.lineNumber === 'number' ? options.lineNumber : 0,
functionName: typeof options.functionName === 'string' ? options.functionName : 'unknown'
};
}

try {
if (options.version === undefined) {
var pkg = require('./package.json');
options.version = pkg.version;
}
} catch (err) {}
if (options.version === undefined) {
options.version = 'unknown';
}

// ErrorEvent
// See https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent
var structPayload = {
// ErrorEvent.serviceContext
serviceContext: {
// ErrorEvent.serviceContext.service
service: 'cloud_function:' + FUNCTION_NAME,
// ErrorEvent.serviceContext.version
version: '' + options.version
},
// ErrorEvent.context
context: context
};

// ErrorEvent.message
if (err instanceof Error && typeof err.stack === 'string') {
structPayload.message = err.stack;
} else if (typeof err === 'string') {
structPayload.message = err;
} else if (typeof err.message === 'string') {
structPayload.message = err.message;
}

log.write(log.entry(resource, structPayload), callback);
}
// [END helloHttpError]

module.exports = reportDetailedError;
76 changes: 75 additions & 1 deletion functions/log/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,82 @@
'use strict';

// [START log]
exports.helloWorld = function (context, data) {
exports.helloWorld = function helloWorld (context, data) {
console.log('I am a log entry!');
context.success();
};
// [END log]

exports.retrieve = function retrieve () {
// [START retrieve]
// By default, gcloud will authenticate using the service account file specified
// by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use the
// project specified by the GCLOUD_PROJECT environment variable. See
// https://googlecloudplatform.github.io/gcloud-node/#/docs/guides/authentication
var gcloud = require('gcloud');
var logging = gcloud.logging();

// Retrieve the latest Cloud Function log entries
// See https://googlecloudplatform.github.io/gcloud-node/#/docs/logging
logging.getEntries({
pageSize: 10,
filter: 'resource.type="cloud_function"'
}, function (err, entries) {
if (err) {
console.error(err);
} else {
console.log(entries);
}
});
// [END retrieve]
};

exports.getMetrics = function getMetrics () {
// [START getMetrics]
var google = require('googleapis');
var monitoring = google.monitoring('v3');

google.auth.getApplicationDefault(function (err, authClient) {
if (err) {
return console.error('Authentication failed', err);
}
if (authClient.createScopedRequired && authClient.createScopedRequired()) {
var scopes = [
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/monitoring',
'https://www.googleapis.com/auth/monitoring.read',
'https://www.googleapis.com/auth/monitoring.write'
];
authClient = authClient.createScoped(scopes);
}

// Format a date according to RFC33339 with milliseconds format
function formatDate (date) {
return JSON.parse(JSON.stringify(date).replace('Z', '000Z'));
}

// Create two datestrings, a start and end range
var oneWeekAgo = new Date();
var now = new Date();
oneWeekAgo.setHours(oneWeekAgo.getHours() - (7 * 24));
oneWeekAgo = formatDate(oneWeekAgo);
now = formatDate(now);

monitoring.projects.timeSeries.list({
auth: authClient,
// There is also cloudfunctions.googleapis.com/function/execution_count
filter: 'metric.type="cloudfunctions.googleapis.com/function/execution_times"',
pageSize: 10,
'interval.startTime': oneWeekAgo,
'interval.endTime': now,
name: 'projects/' + process.env.GCLOUD_PROJECT
}, function (err, results) {
if (err) {
console.error(err);
} else {
console.log(results.timeSeries);
}
});
});
// [END getMetrics]
};
Loading