Skip to content

feat(v8/serverless): Use subpath exports package #10561

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

Closed
wants to merge 6 commits into from
Closed
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
31 changes: 31 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,34 @@ enum. If you were using the `Severity` enum, you should replace it with the `Sev
The `Offline` integration has been removed in favor of the offline transport wrapper:
http://docs.sentry.io/platforms/javascript/configuration/transports/#offline-caching

## Updating `@sentry/serverless` exports

The `AWSServices`, `GoogleCloudGrpc`, and `GoogleCloudHttp`. integrations have been removed from `@sentry/serverless` package. See the [table below](#deprecate-class-based-integrations) to find what to upgrade to.

We've also updated the package to use subpath exports instead of namespace exports with `Sentry.AWSLambda` and `Sentry.GCPFunction`. This helps with tree-shaking and organizes the code better. Below is an example of how your imports should look after the upgrade:

```js
// before
import * as Sentry from '@sentry/serverless';

Sentry.AWSLambda.init(...);

// after
import * as Sentry from '@sentry/serverless/aws';

Sentry.init(...);

// before
import * as Sentry from '@sentry/serverless';

Sentry.GCPFunction.init(...);

// after
import * as Sentry from '@sentry/serverless/gcp';

Sentry.init(...);
```

# Deprecations in 7.x

You can use the **Experimental** [@sentry/migr8](https://www.npmjs.com/package/@sentry/migr8) to automatically update
Expand Down Expand Up @@ -210,6 +238,9 @@ The following list shows how integrations should be migrated:
| `new Hapi()` | `hapiIntegration()` | `@sentry/node` |
| `new Undici()` | `nativeNodeFetchIntegration()` | `@sentry/node` |
| `new Http()` | `httpIntegration()` | `@sentry/node` |
| `new AWSServices()` | `awsServicesIntegration()` | `@sentry/serverless` |
| `new GoogleCloudGrpc()` | `googleCloudGrpcIntegration()` | `@sentry/serverless` |
| `new GoogleCloudHttp()` | `googleCloudHttpIntegration()` | `@sentry/serverless` |

## Deprecate `hub.bindClient()` and `makeMain()`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ const DEPENDENTS: Dependent[] = [
exports: Object.keys(SentryRemix),
},
{
package: '@sentry/serverless',
package: '@sentry/serverless/aws',
exports: Object.keys(SentryServerless),
ignoreExports: ['cron', 'hapiErrorPlugin', 'enableAnrDetection'],
Copy link
Member Author

Choose a reason for hiding this comment

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

I will add another e2e test to validate the subpath exports if we are fine with this approach.

},
{
package: '@sentry/serverless/gcp',
exports: Object.keys(SentryServerless),
ignoreExports: ['cron', 'hapiErrorPlugin', 'enableAnrDetection'],
},
Expand Down
31 changes: 14 additions & 17 deletions packages/serverless/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,37 @@

## General

This package is a wrapper around `@sentry/node`, with added functionality related to various Serverless solutions. All
methods available in `@sentry/node` can be imported from `@sentry/serverless`.

Currently supported environment:
This package is a wrapper around `@sentry/node`, with added functionality related to various Serverless solutions. Currently AWS Lambda and Google Cloud Functions are supported.

### AWS Lambda

To use this SDK, call `Sentry.AWSLambda.init(options)` at the very beginning of your JavaScript file.
To use this SDK, import `Sentry.init` from `@sentry/serverless/aws` at the very beginning of your JavaScript file.

```javascript
import * as Sentry from '@sentry/serverless';
import * as Sentry from '@sentry/serverless/aws';

Sentry.AWSLambda.init({
Sentry.init({
dsn: '__DSN__',
// ...
});

// async (recommended)
exports.handler = Sentry.AWSLambda.wrapHandler(async (event, context) => {
exports.handler = Sentry.wrapHandler(async (event, context) => {
throw new Error('oh, hello there!');
});

// sync
exports.handler = Sentry.AWSLambda.wrapHandler((event, context, callback) => {
exports.handler = Sentry.wrapHandler((event, context, callback) => {
throw new Error('oh, hello there!');
});
```

If you also want to trace performance of all the incoming requests and also outgoing AWS service requests, just set the `tracesSampleRate` option.

```javascript
import * as Sentry from '@sentry/serverless';
import * as Sentry from '@sentry/serverless/aws';

Sentry.AWSLambda.init({
Sentry.init({
dsn: '__DSN__',
tracesSampleRate: 1.0,
});
Expand All @@ -65,32 +62,32 @@ Another and much simpler way to integrate Sentry to your AWS Lambda function is

### Google Cloud Functions

To use this SDK, call `Sentry.GCPFunction.init(options)` at the very beginning of your JavaScript file.
To use this SDK, import `Sentry.init` from `@sentry/serverless/gcp` at the very beginning of your JavaScript file.

```javascript
import * as Sentry from '@sentry/serverless';
import * as Sentry from '@sentry/serverless/gcp';

Sentry.GCPFunction.init({
Sentry.init({
dsn: '__DSN__',
tracesSampleRate: 1.0,
// ...
});

// For HTTP Functions:

exports.helloHttp = Sentry.GCPFunction.wrapHttpFunction((req, res) => {
exports.helloHttp = Sentry.wrapHttpFunction((req, res) => {
throw new Error('oh, hello there!');
});

// For Background Functions:

exports.helloEvents = Sentry.GCPFunction.wrapEventFunction((data, context, callback) => {
exports.helloEvents = Sentry.wrapEventFunction((data, context, callback) => {
throw new Error('oh, hello there!');
});

// For CloudEvents:

exports.helloEvents = Sentry.GCPFunction.wrapCloudEventFunction((context, callback) => {
exports.helloEvents = Sentry.wrapCloudEventFunction((context, callback) => {
throw new Error('oh, hello there!');
});
```
39 changes: 39 additions & 0 deletions packages/serverless/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,49 @@
"main": "build/npm/cjs/index.js",
"module": "build/npm/esm/index.js",
"types": "build/npm/types/index.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"default": "./build/npm/esm/index.js",
"types": "./build/npm/types/index.d.ts"
Copy link
Member Author

Choose a reason for hiding this comment

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

in the future this will be *.mjs fwiw

},
"require": {
"default": "./build/npm/cjs/index.js",
"types": "./build/npm/types/index.d.ts"
}
},
"./aws": {
"import": {
"default": "./build/npm/esm/index.aws.js",
"types": "./build/npm/types/index.aws.d.ts"
},
"require": {
"default": "./build/npm/cjs/index.aws.js",
"types": "./build/npm/types/index.aws.d.ts"
}
},
"./gcp": {
"import": {
"default": "./build/npm/esm/index.gcp.js",
"types": "./build/npm/types/index.gcp.d.ts"
},
"require": {
"default": "./build/npm/cjs/index.gcp.js",
"types": "./build/npm/types/index.gcp.d.ts"
}
}
},
"typesVersions": {
"<4.9": {
"build/npm/types/index.d.ts": [
"build/npm/types-ts3.8/index.d.ts"
],
"build/npm/types/index.aws.d.ts": [
"build/npm/types-ts3.8/index.aws.d.ts"
],
"build/npm/types/index.gcp.d.ts": [
"build/npm/types-ts3.8/index.gcp.d.ts"
]
}
},
Expand Down
2 changes: 1 addition & 1 deletion packages/serverless/rollup.aws.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default [
makeBaseBundleConfig({
// this automatically sets it to be CJS
bundleType: 'node',
entrypoints: ['src/index.awslambda.ts'],
entrypoints: ['src/index.aws.ts'],
jsVersion: 'es6',
licenseTitle: '@sentry/serverless',
outputFileBase: () => 'index',
Expand Down
5 changes: 1 addition & 4 deletions packages/serverless/rollup.npm.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollu

export default makeNPMConfigVariants(
makeBaseNPMConfig({
// TODO: `awslambda-auto.ts` is a file which the lambda layer uses to automatically init the SDK. Does it need to be
// in the npm package? Is it possible that some people are using it themselves in the same way the layer uses it (in
// which case removing it would be a breaking change)? Should it stay here or not?
entrypoints: ['src/index.ts', 'src/awslambda-auto.ts'],
entrypoints: ['src/index.aws.ts', 'src/index.gcp.ts'],
// packages with bundles have a different build directory structure
hasBundles: true,
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, convertIntegrationFnToClass, defineIntegration } from '@sentry/core';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration } from '@sentry/core';
import { getClient, startInactiveSpan } from '@sentry/node';
import type { Client, Integration, IntegrationClass, IntegrationFn, Span } from '@sentry/types';
import type { Client, IntegrationFn, Span } from '@sentry/types';
import { fill } from '@sentry/utils';
// 'aws-sdk/global' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import type * as AWS from 'aws-sdk/global';

type GenericParams = { [key: string]: any }; // eslint-disable-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -41,21 +39,10 @@ const _awsServicesIntegration = ((options: { optional?: boolean } = {}) => {
};
}) satisfies IntegrationFn;

export const awsServicesIntegration = defineIntegration(_awsServicesIntegration);

/**
* AWS Service Request Tracking.
*
* @deprecated Use `awsServicesIntegration()` instead.
*/
// eslint-disable-next-line deprecation/deprecation
export const AWSServices = convertIntegrationFnToClass(
INTEGRATION_NAME,
awsServicesIntegration,
) as IntegrationClass<Integration>;

// eslint-disable-next-line deprecation/deprecation
export type AWSServices = typeof AWSServices;
export const awsServicesIntegration = defineIntegration(_awsServicesIntegration);

/**
* Patches AWS SDK request to create `http.client` spans.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
captureException,
captureMessage,
continueTrace,
defaultIntegrations as nodeDefaultIntegrations,
flush,
getCurrentScope,
getDefaultIntegrations as getNodeDefaultIntegrations,
Expand All @@ -24,10 +23,8 @@ import { performance } from 'perf_hooks';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
import { awsServicesIntegration } from './awsservices';

import { DEBUG_BUILD } from './debug-build';
import { markEventUnhandled } from './utils';

export * from '@sentry/node';
import { DEBUG_BUILD } from '../debug-build';
import { markEventUnhandled } from '../utils';

const { isPromise } = types;

Expand All @@ -38,7 +35,7 @@ type SyncHandler<T extends Handler> = (
callback: Parameters<T>[2],
) => void;

export type AsyncHandler<T extends Handler> = (
type AsyncHandler<T extends Handler> = (
event: Parameters<T>[0],
context: Parameters<T>[1],
) => Promise<NonNullable<Parameters<Parameters<T>[2]>[1]>>;
Expand Down Expand Up @@ -66,13 +63,6 @@ export interface WrapperOptions {
startTrace: boolean;
}

/** @deprecated Use `getDefaultIntegrations(options)` instead. */
export const defaultIntegrations: Integration[] = [
// eslint-disable-next-line deprecation/deprecation
...nodeDefaultIntegrations,
awsServicesIntegration({ optional: true }),
];

/** Get the default integrations for the AWSLambda SDK. */
export function getDefaultIntegrations(options: Options): Integration[] {
return [...getNodeDefaultIntegrations(options), awsServicesIntegration({ optional: true })];
Expand Down
6 changes: 3 additions & 3 deletions packages/serverless/src/awslambda-auto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as Sentry from './index';
import { init, tryPatchHandler } from './aws/sdk';

const lambdaTaskRoot = process.env.LAMBDA_TASK_ROOT;
if (lambdaTaskRoot) {
Expand All @@ -7,11 +7,11 @@ if (lambdaTaskRoot) {
throw Error(`LAMBDA_TASK_ROOT is non-empty(${lambdaTaskRoot}) but _HANDLER is not set`);
}

Sentry.AWSLambda.init({
init({
_invokedByLambdaLayer: true,
});

Sentry.AWSLambda.tryPatchHandler(lambdaTaskRoot, handlerString);
tryPatchHandler(lambdaTaskRoot, handlerString);
} else {
throw Error('LAMBDA_TASK_ROOT environment variable is not set');
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import type { EventEmitter } from 'events';
import {
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
convertIntegrationFnToClass,
defineIntegration,
getClient,
} from '@sentry/core';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, getClient } from '@sentry/core';
import { startInactiveSpan } from '@sentry/node';
import type { Client, Integration, IntegrationClass, IntegrationFn } from '@sentry/types';
import type { Client, IntegrationFn } from '@sentry/types';
import { fill } from '@sentry/utils';

interface GrpcFunction extends CallableFunction {
Expand Down Expand Up @@ -62,21 +57,10 @@ const _googleCloudGrpcIntegration = ((options: { optional?: boolean } = {}) => {
};
}) satisfies IntegrationFn;

export const googleCloudGrpcIntegration = defineIntegration(_googleCloudGrpcIntegration);

/**
* Google Cloud Platform service requests tracking for GRPC APIs.
*
* @deprecated Use `googleCloudGrpcIntegration()` instead.
*/
// eslint-disable-next-line deprecation/deprecation
export const GoogleCloudGrpc = convertIntegrationFnToClass(
INTEGRATION_NAME,
googleCloudGrpcIntegration,
) as IntegrationClass<Integration>;

// eslint-disable-next-line deprecation/deprecation
export type GoogleCloudGrpc = typeof GoogleCloudGrpc;
export const googleCloudGrpcIntegration = defineIntegration(_googleCloudGrpcIntegration);

/** Returns a wrapped function that returns a stub with tracing enabled */
function wrapCreateStub(origCreate: CreateStubFunc): CreateStubFunc {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import type * as common from '@google-cloud/common';
import {
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
convertIntegrationFnToClass,
defineIntegration,
getClient,
} from '@sentry/core';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, getClient } from '@sentry/core';
import { startInactiveSpan } from '@sentry/node';
import type { Client, Integration, IntegrationClass, IntegrationFn } from '@sentry/types';
import type { Client, IntegrationFn } from '@sentry/types';
import { fill } from '@sentry/utils';

type RequestOptions = common.DecorateRequestOptions;
Expand Down Expand Up @@ -41,21 +36,10 @@ const _googleCloudHttpIntegration = ((options: { optional?: boolean } = {}) => {
};
}) satisfies IntegrationFn;

export const googleCloudHttpIntegration = defineIntegration(_googleCloudHttpIntegration);

/**
* Google Cloud Platform service requests tracking for RESTful APIs.
*
* @deprecated Use `googleCloudHttpIntegration()` instead.
*/
// eslint-disable-next-line deprecation/deprecation
export const GoogleCloudHttp = convertIntegrationFnToClass(
INTEGRATION_NAME,
googleCloudHttpIntegration,
) as IntegrationClass<Integration>;

// eslint-disable-next-line deprecation/deprecation
export type GoogleCloudHttp = typeof GoogleCloudHttp;
export const googleCloudHttpIntegration = defineIntegration(_googleCloudHttpIntegration);

/** Returns a wrapped function that makes a request with tracing enabled */
function wrapRequestFunction(orig: RequestFunction): RequestFunction {
Expand Down
Loading