Skip to content

Commit 249a313

Browse files
authored
feat(google-cloud): Add @sentry/google-cloud package (#10993)
in the first step to our serverless changes, introduce a `@sentry/google-cloud` package that contains all google cloud functions related functionality. No migration guide yet, will write that after I merge this in and open a PR for the aws changes.
1 parent 0caddec commit 249a313

33 files changed

+1888
-1
lines changed

.craft.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ targets:
9191
- name: npm
9292
id: '@sentry/serverless'
9393
includeNames: /^sentry-serverless-\d.*\.tgz$/
94+
- name: npm
95+
id: '@sentry/google-cloud'
96+
includeNames: /^sentry-google-cloud-\d.*\.tgz$/
9497
- name: npm
9598
id: '@sentry/bun'
9699
includeNames: /^sentry-bun-\d.*\.tgz$/

.github/ISSUE_TEMPLATE/bug.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ body:
4343
- '@sentry/react'
4444
- '@sentry/remix'
4545
- '@sentry/serverless'
46+
- '@sentry/google-cloud'
4647
- '@sentry/svelte'
4748
- '@sentry/sveltekit'
4849
- '@sentry/vue'

dev-packages/e2e-tests/test-applications/node-exports-test-app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@sentry/astro": "latest || *",
2020
"@sentry/nextjs": "latest || *",
2121
"@sentry/serverless": "latest || *",
22+
"@sentry/google-cloud": "latest || *",
2223
"@sentry/bun": "latest || *",
2324
"@sentry/types": "latest || *",
2425
"@types/node": "18.15.1",

dev-packages/e2e-tests/test-applications/node-exports-test-app/scripts/consistentExports.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as SentryAstro from '@sentry/astro';
22
import * as SentryBun from '@sentry/bun';
3+
import * as SentryGoogleCloud from '@sentry/google-cloud';
34
import * as SentryNextJs from '@sentry/nextjs';
45
import * as SentryNode from '@sentry/node';
56
import * as SentryNodeExperimental from '@sentry/node-experimental';
@@ -81,6 +82,12 @@ const DEPENDENTS: Dependent[] = [
8182
exports: Object.keys(SentryServerless),
8283
ignoreExports: ['cron', 'hapiErrorPlugin'],
8384
},
85+
{
86+
package: '@sentry/google-cloud',
87+
compareWith: nodeExports,
88+
exports: Object.keys(SentryGoogleCloud),
89+
ignoreExports: ['makeMain'],
90+
},
8491
{
8592
package: '@sentry/sveltekit',
8693
compareWith: nodeExperimentalExports,

dev-packages/e2e-tests/verdaccio-config/config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ packages:
140140
unpublish: $all
141141
# proxy: npmjs # Don't proxy for E2E tests!
142142

143+
'@sentry/google-cloud':
144+
access: $all
145+
publish: $all
146+
unpublish: $all
147+
# proxy: npmjs # Don't proxy for E2E tests!
148+
143149
'@sentry/svelte':
144150
access: $all
145151
publish: $all

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"postpublish": "lerna run --stream --concurrency 1 postpublish",
3434
"test": "lerna run --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\" test",
3535
"test:unit": "lerna run --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\" test:unit",
36-
"test-ci-browser": "lerna run test --ignore \"@sentry/{bun,deno,node,node-experimental,profiling-node,serverless,nextjs,remix,gatsby,sveltekit,vercel-edge}\" --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\"",
36+
"test-ci-browser": "lerna run test --ignore \"@sentry/{bun,deno,node,node-experimental,profiling-node,serverless,google-cloud,nextjs,remix,gatsby,sveltekit,vercel-edge}\" --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\"",
3737
"test-ci-node": "ts-node ./scripts/node-unit-tests.ts",
3838
"test-ci-bun": "lerna run test --scope @sentry/bun",
3939
"test:update-snapshots": "lerna run test:update-snapshots",
@@ -56,6 +56,7 @@
5656
"packages/eslint-plugin-sdk",
5757
"packages/feedback",
5858
"packages/gatsby",
59+
"packages/google-cloud",
5960
"packages/integration-shims",
6061
"packages/nextjs",
6162
"packages/node",

packages/google-cloud/.eslintrc.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
module.exports = {
2+
env: {
3+
node: true,
4+
},
5+
extends: ['../../.eslintrc.js'],
6+
rules: {
7+
'@sentry-internal/sdk/no-optional-chaining': 'off',
8+
},
9+
overrides: [
10+
{
11+
files: ['scripts/**/*.ts'],
12+
parserOptions: {
13+
project: ['../../tsconfig.dev.json'],
14+
},
15+
},
16+
{
17+
files: ['test/**'],
18+
parserOptions: {
19+
sourceType: 'module',
20+
},
21+
},
22+
],
23+
};

packages/google-cloud/LICENSE

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Copyright (c) 2024 Sentry (https://sentry.io) and individual contributors. All rights reserved.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4+
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
5+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6+
persons to whom the Software is furnished to do so, subject to the following conditions:
7+
8+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
9+
Software.
10+
11+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
12+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

packages/google-cloud/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<p align="center">
2+
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
3+
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84">
4+
</a>
5+
</p>
6+
7+
# Official Sentry SDK for Google Cloud Functions
8+
9+
## Links
10+
11+
- [Official SDK Docs](https://docs.sentry.io/)
12+
- [TypeDoc](http://getsentry.github.io/sentry-javascript/)
13+
14+
## General
15+
16+
This package is a wrapper around `@sentry/node`, with added functionality related to various Serverless solutions. All
17+
methods available in `@sentry/node` can be imported from `@sentry/google-cloud`.
18+
19+
To use this SDK, call `Sentry.init(options)` at the very beginning of your JavaScript file.
20+
21+
```javascript
22+
import * as Sentry from '@sentry/google-cloud';
23+
24+
Sentry.init({
25+
dsn: '__DSN__',
26+
tracesSampleRate: 1.0,
27+
// ...
28+
});
29+
30+
// For HTTP Functions:
31+
32+
exports.helloHttp = Sentry.wrapHttpFunction((req, res) => {
33+
throw new Error('oh, hello there!');
34+
});
35+
36+
// For Background Functions:
37+
38+
exports.helloEvents = Sentry.wrapEventFunction((data, context, callback) => {
39+
throw new Error('oh, hello there!');
40+
});
41+
42+
// For CloudEvents:
43+
44+
exports.helloEvents = Sentry.wrapCloudEventFunction((context, callback) => {
45+
throw new Error('oh, hello there!');
46+
});
47+
```

packages/google-cloud/jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('../../jest/jest.config.js');

packages/google-cloud/package.json

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{
2+
"name": "@sentry/google-cloud",
3+
"version": "8.0.0-alpha.2",
4+
"description": "Official Sentry SDK for Google Cloud Functions",
5+
"repository": "git://github.com/getsentry/sentry-javascript.git",
6+
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/google-cloud",
7+
"author": "Sentry",
8+
"license": "MIT",
9+
"engines": {
10+
"node": ">=14.18"
11+
},
12+
"files": [
13+
"cjs",
14+
"esm",
15+
"types",
16+
"types-ts3.8"
17+
],
18+
"main": "build/cjs/index.js",
19+
"module": "build/esm/index.js",
20+
"types": "build/types/index.d.ts",
21+
"exports": {
22+
"./package.json": "./package.json",
23+
".": {
24+
"import": {
25+
"types": "./build/types/index.d.ts",
26+
"default": "./build/esm/index.js"
27+
},
28+
"require": {
29+
"types": "./build/types/index.d.ts",
30+
"default": "./build/cjs/index.js"
31+
}
32+
}
33+
},
34+
"typesVersions": {
35+
"<4.9": {
36+
"build/types/index.d.ts": [
37+
"build/types-ts3.8/index.d.ts"
38+
]
39+
}
40+
},
41+
"publishConfig": {
42+
"access": "public"
43+
},
44+
"dependencies": {
45+
"@sentry/core": "8.0.0-alpha.2",
46+
"@sentry/node": "8.0.0-alpha.2",
47+
"@sentry/types": "8.0.0-alpha.2",
48+
"@sentry/utils": "8.0.0-alpha.2",
49+
"@types/express": "^4.17.14"
50+
},
51+
"devDependencies": {
52+
"@google-cloud/bigquery": "^5.3.0",
53+
"@google-cloud/common": "^3.4.1",
54+
"@google-cloud/functions-framework": "^1.7.1",
55+
"@google-cloud/pubsub": "^2.5.0",
56+
"@types/node": "^14.6.4",
57+
"find-up": "^5.0.0",
58+
"google-gax": "^2.9.0",
59+
"nock": "^13.0.4",
60+
"npm-packlist": "^2.1.4"
61+
},
62+
"scripts": {
63+
"build": "run-p build:transpile build:types",
64+
"build:dev": "yarn build",
65+
"build:transpile": "rollup -c rollup.npm.config.mjs",
66+
"build:types": "run-s build:types:core build:types:downlevel",
67+
"build:types:core": "tsc -p tsconfig.types.json",
68+
"build:types:downlevel": "yarn downlevel-dts build/types build/types-ts3.8 --to ts3.8",
69+
"build:watch": "run-p build:transpile:watch build:types:watch",
70+
"build:dev:watch": "yarn build:watch",
71+
"build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch",
72+
"build:types:watch": "tsc -p tsconfig.types.json --watch",
73+
"build:tarball": "ts-node ../../scripts/prepack.ts && npm pack ./build",
74+
"circularDepCheck": "madge --circular src/index.ts",
75+
"clean": "rimraf build coverage sentry-google-cloud-*.tgz",
76+
"fix": "eslint . --format stylish --fix",
77+
"lint": "eslint . --format stylish",
78+
"test": "jest",
79+
"test:watch": "jest --watch",
80+
"yalc:publish": "ts-node ../../scripts/prepack.ts --bundles && yalc publish ./build/npm --push --sig"
81+
},
82+
"volta": {
83+
"extends": "../../package.json"
84+
},
85+
"sideEffects": false
86+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils';
2+
3+
export default makeNPMConfigVariants(makeBaseNPMConfig());
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
declare const __DEBUG_BUILD__: boolean;
2+
3+
/**
4+
* This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.
5+
*
6+
* ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
7+
*/
8+
export const DEBUG_BUILD = __DEBUG_BUILD__;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, handleCallbackErrors } from '@sentry/core';
2+
import { captureException, flush, getCurrentScope, startSpanManual } from '@sentry/node';
3+
import { logger } from '@sentry/utils';
4+
5+
import { DEBUG_BUILD } from '../debug-build';
6+
import { domainify, markEventUnhandled, proxyFunction } from '../utils';
7+
import type { CloudEventFunction, CloudEventFunctionWithCallback, WrapperOptions } from './general';
8+
9+
export type CloudEventFunctionWrapperOptions = WrapperOptions;
10+
11+
/**
12+
* Wraps an event function handler adding it error capture and tracing capabilities.
13+
*
14+
* @param fn Event handler
15+
* @param options Options
16+
* @returns Event handler
17+
*/
18+
export function wrapCloudEventFunction(
19+
fn: CloudEventFunction | CloudEventFunctionWithCallback,
20+
wrapOptions: Partial<CloudEventFunctionWrapperOptions> = {},
21+
): CloudEventFunctionWithCallback {
22+
return proxyFunction(fn, f => domainify(_wrapCloudEventFunction(f, wrapOptions)));
23+
}
24+
25+
function _wrapCloudEventFunction(
26+
fn: CloudEventFunction | CloudEventFunctionWithCallback,
27+
wrapOptions: Partial<CloudEventFunctionWrapperOptions> = {},
28+
): CloudEventFunctionWithCallback {
29+
const options: CloudEventFunctionWrapperOptions = {
30+
flushTimeout: 2000,
31+
...wrapOptions,
32+
};
33+
return (context, callback) => {
34+
return startSpanManual(
35+
{
36+
name: context.type || '<unknown>',
37+
op: 'function.gcp.cloud_event',
38+
attributes: {
39+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component',
40+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.serverless.gcp_cloud_event',
41+
},
42+
},
43+
span => {
44+
const scope = getCurrentScope();
45+
scope.setContext('gcp.function.context', { ...context });
46+
47+
const newCallback = domainify((...args: unknown[]) => {
48+
if (args[0] !== null && args[0] !== undefined) {
49+
captureException(args[0], scope => markEventUnhandled(scope));
50+
}
51+
span.end();
52+
53+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
54+
flush(options.flushTimeout)
55+
.then(null, e => {
56+
DEBUG_BUILD && logger.error(e);
57+
})
58+
.then(() => {
59+
callback(...args);
60+
});
61+
});
62+
63+
if (fn.length > 1) {
64+
return handleCallbackErrors(
65+
() => (fn as CloudEventFunctionWithCallback)(context, newCallback),
66+
err => {
67+
captureException(err, scope => markEventUnhandled(scope));
68+
},
69+
);
70+
}
71+
72+
return Promise.resolve()
73+
.then(() => (fn as CloudEventFunction)(context))
74+
.then(
75+
result => newCallback(null, result),
76+
err => newCallback(err, undefined),
77+
);
78+
},
79+
);
80+
};
81+
}

0 commit comments

Comments
 (0)