diff --git a/types/newrelic/index.d.ts b/types/newrelic/index.d.ts new file mode 100644 index 0000000000..83b6e409dc --- /dev/null +++ b/types/newrelic/index.d.ts @@ -0,0 +1,331 @@ +// Type definitions for newrelic 2.7 +// Project: http://github.com/newrelic/node-newrelic +// Definitions by: Matt R. Wilson +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +// https://docs.newrelic.com/docs/agents/nodejs-agent/api-guides/nodejs-agent-api +declare namespace newrelic { + interface NewRelicAPI { + /** + * Give the current transaction a custom name. + * + * Overrides any New Relic naming rules set in configuration or from New Relic's servers. + * + * IMPORTANT: this function must be called when a transaction is active. New + * Relic transactions are tied to web requests, so this method may be called + * from within HTTP or HTTPS listener functions, Express routes, or other + * contexts where a web request or response object are in scope. + * + * The `name` will be prefixed with 'Custom/' when sent. + */ + setTransactionName(name: string): void; + + /** + * Returns a handle on the currently executing transaction. + * + * This handle can then be used to end or ignore a given transaction safely from any context. + * It is best used with newrelic.startWebTransaction() and newrelic.startBackgroundTransaction(). + */ + getTransaction(): TransactionHandle; + + /** + * Specify the `Dispatcher` and `Dispatcher Version` environment values. + * + * A dispatcher is typically the service responsible for brokering + * the request with the process responsible for responding to the + * request. For example Node's `http` module would be the dispatcher + * for incoming HTTP requests. + */ + setDispatcher(name: string, version?: string): void; + + /** + * Give the current transaction a name based on your own idea of what + * constitutes a controller in your Node application. Also allows you to + * optionally specify the action being invoked on the controller. If the action + * is omitted, then the API will default to using the HTTP method used in the + * request (e.g. GET, POST, DELETE). Overrides any New Relic naming rules set + * in configuration or from New Relic's servers. + * + * IMPORTANT: this function must be called when a transaction is active. New + * Relic transactions are tied to web requests, so this method may be called + * from within HTTP or HTTPS listener functions, Express routes, or other + * contexts where a web request or response object are in scope. + * + * The `name` will be prefixed with 'Controller/' when sent. + * The `action` defaults to the HTTP method used for the request. + */ + setControllerName(name: string, action: string): void; + + /** + * Add a custom attribute to the current transaction. + * + * Some attributes are reserved (see CUSTOM_BLACKLIST in the docs for the current, very short list), and + * as with most API methods, this must be called in the context of an + * active transaction. + * + * Most recently set value wins. + */ + addCustomAttribute(key: string, value: string): void; + + /** + * Adds all custom attributes in an object to the current transaction. + * + * See documentation for `addCustomAttribute` for more information on setting custom attributes. + */ + addCustomAttributes(atts: { [key: string]: string }): void; + + /** + * Tell the tracer whether to ignore the current transaction. + * + * The most common use for this will be to mark a transaction as ignored (maybe it's handling + * a websocket polling channel, or maybe it's an external call you don't care + * is slow), but it's also useful when you want a transaction that would + * otherwise be ignored due to URL or transaction name normalization rules + * to *not* be ignored. + */ + setIgnoreTransaction(ignored: boolean): void; + + /** + * Send errors to New Relic that you've already handled yourself. + * + * NOTE: Errors that are recorded using this method do _not_ obey the `ignore_status_codes` configuration. + * + * Optional. Any custom attributes to be displayed in the New Relic UI. + */ + noticeError(error: Error, customAttributes?: { [key: string]: string }): void; + + /** + * If the URL for a transaction matches the provided pattern, name the + * transaction with the provided name. + * + * If there are capture groups in the pattern (which is a standard JavaScript regular expression, + * and can be passed as either a RegExp or a string), then the substring matches ($1, $2, + * etc.) are replaced in the name string. BE CAREFUL WHEN USING SUBSTITUTION. + * If the replacement substrings are highly variable (i.e. are identifiers, + * GUIDs, or timestamps), the rule will generate too many metrics and + * potentially get your application blacklisted by New Relic. + * + * An example of a good rule with replacements: + * + * newrelic.addNamingRule('^/storefront/(v[1-5])/(item|category|tag)', 'CommerceAPI/$1/$2') + * + * An example of a bad rule with replacements: + * + * newrelic.addNamingRule('^/item/([0-9a-f]+)', 'Item/$1') + * + * Keep in mind that the original URL and any query parameters will be sent + * along with the request, so slow transactions will still be identifiable. + * + * Naming rules can not be removed once added. They can also be added via the + * agent's configuration. See configuration documentation for details. + */ + addNamingRule(pattern: RegExp | string, name: string): void; + + /** + * If the URL for a transaction matches the provided pattern, ignore the transaction attached to that URL. + * + * Useful for filtering socket.io connections and other long-polling requests out of your agents to keep + * them from distorting an app's apdex or mean response time. + * + * Example: + * + * newrelic.addIgnoringRule('^/socket\\.io/') + */ + addIgnoringRule(pattern: RegExp | string): void; + + /** + * Get the header necessary for Browser Monitoring. + * + * This script must be manually injected into your templates, as high as possible + * in the header, but _after_ any X-UA-COMPATIBLE HTTP-EQUIV meta tags. + * Otherwise you may hurt IE! + * + * This method must be called _during_ a transaction, and must be called every + * time you want to generate the headers. + * + * Do *not* reuse the headers between users, or even between requests. + */ + getBrowserTimingHeader(): string; + + /** + * Instrument a particular callback to improve visibility into a transaction. + * + * Use this API call to improve instrumentation of a particular method, or to track work across asynchronous + * boundaries by calling createTracer() in both the target function and its parent asynchronous function. + * + * The name will be visible in transaction traces and as a new metric in the New Relic UI. + * + * The agent begins timing the segment when createTracer is called, and ends the segment when the callback + * defined by the callback argument finishes executing. + */ + createTracer any>(name: string, handle: T): T; + + /** + * Creates and starts a web transaction to record work done in the handle supplied. + * + * This transaction will run until the handle + * synchronously returns UNLESS: + * 1. The handle function returns a promise, where the end of the + * transaction will be tied to the end of the promise returned. + * 2. `getTransaction` is called in the handle, flagging the + * transaction as externally handled. In this case the transaction + * will be ended when `TransactionHandle#end` is called in the user's code. + * + * @example + * var newrelic = require('newrelic') + * newrelic.startWebTransaction('/some/url/path', function() { + * var transaction = newrelic.getTransaction() + * setTimeout(function() { + * // do some work + * transaction.end() + * }, 100) + * }) + * + * The `url` is used to name and group related transactions in APM, + * so it should be a generic name and not include any variable parameters. + */ + startWebTransaction(url: string, handle: (...args: any[]) => any): any; + + /** + * Creates and starts a background transaction to record work done in the handle supplied. + * + * This transaction will run until the handle + * synchronously returns UNLESS: + * 1. The handle function returns a promise, where the end of the + * transaction will be tied to the end of the promise returned. + * 2. `API#getTransaction` is called in the handle, flagging the + * transaction as externally handled. In this case the transaction + * will be ended when `TransactionHandle#end` is called in the user's code. + * + * @example + * var newrelic = require('newrelic') + * newrelic.startBackgroundTransaction('Red October', 'Subs', function() { + * var transaction = newrelic.getTransaction() + * setTimeout(function() { + * // do some work + * transaction.end() + * }, 100) + * }) + * + * The `url` is used to name and group related transactions in APM, + * so it should be a generic name and not include any variable parameters. + * + * The optional `group can be used for grouping background transactions in APM. + * For more information see: + * https://docs.newrelic.com/docs/apm/applications-menu/monitoring/transactions-page#txn-type-dropdown + */ + startBackgroundTransaction(name: string, handle: (...args: any[]) => any): any; + startBackgroundTransaction(name: string, group: string, handle: (...args: any[]) => any): any; + + /** + * End the current web or background custom transaction. + * + * This method requires being in the correct transaction context when called. + */ + endTransaction(): void; + + /** + * Record an event-based metric, usually associated with a particular duration. + * + * The `name` must be a string following standard metric naming rules. The `value` will + * usually be a number, but it can also be an object. + * * When `value` is a numeric value, it should represent the magnitude of a measurement + * associated with an event; for example, the duration for a particular method call. + * * When `value` is an object, it must contain count, total, min, max, and sumOfSquares + * keys, all with number values. This form is useful to aggregate metrics on your own + * and report them periodically; for example, from a setInterval. These values will + * be aggregated with any previously collected values for the same metric. The names + * of these keys match the names of the keys used by the platform API. + */ + recordMetric(name: string, value: number | Metric): void; + + /** + * Update a metric that acts as a simple counter. + * + * The count of the selected metric will be incremented by the specified amount, defaulting to 1. + */ + incrementMetric(name: string, value?: number): void; + + /** + * Record an event-based metric, usually associated with a particular duration. + * + * `eventType` must be an alphanumeric string less than 255 characters. + * The keys of `attributes` must be shorter than 255 characters. + */ + recordCustomEvent(eventType: string, attributes: { [keys: string]: boolean | number | string }): void; + + /** + * Registers an instrumentation function. + * + * The provided onRequire callback will be fired when the given module is loaded with require. + * The moduleName parameter should be the string that will be passed to require; + * for example, 'express' or 'amqplib/callback_api'. + * + * The optional onError callback is called if the onRequire parameters throws an error. + * This is useful for debugging your instrumentation. + * + * Use this method to: + * - Add instrumentation for modules not currently instrumented by New Relic. + * - Instrument your own code. + * - Replace the Node.js agent's built-in instrumentation with your own. + */ + instrument: Instrument; + + /** + * Sets an instrumentation callback for a datastore module. + * + * This method is just like `instrument`, except it provides a datastore-service-specialized shim. + */ + instrumentDatastore: Instrument; + + /** + * Sets an instrumentation callback for a web framework module. + * + * This method is just like `instrument`, except it provides a web-framework-specialized shim. + */ + instrumentWebframework: Instrument; + + /** + * Sets an instrumentation callback for a message service client module. + * + * This method is just like `instrument`, except it provides a message-service-specialized shim. + */ + instrumentMessages: Instrument; + + /** + * Gracefully shuts down the agent. + * + * If `collectPendingData` is true, the agent will send any pending data to the collector + * before shutting down. Defaults to `false`. + */ + shutdown(cb?: (error?: Error) => void): void; + shutdown(options?: { collectPendingData?: boolean, timeout?: number }, cb?: (error?: Error) => void): void; + } + + interface Instrument { + (opts: { moduleName: string, onRequire: () => void, onError?: (err: Error) => void }): void; + (moduleName: string, onRequire: () => void, onError?: (err: Error) => void): void; + } + + interface Metric { + count: number; + total: number; + min: number; + max: number; + sumOfSquares: number; + } + + interface TransactionHandle { + /** + * End the transaction. + */ + end(callback?: () => any): void; + + /** + * Mark the transaction to be ignored. + */ + ignore(): void; + } +} +declare const api: newrelic.NewRelicAPI; +export = api; diff --git a/types/newrelic/newrelic-tests.ts b/types/newrelic/newrelic-tests.ts new file mode 100644 index 0000000000..73cd53cc78 --- /dev/null +++ b/types/newrelic/newrelic-tests.ts @@ -0,0 +1,98 @@ +/// +import * as newrelic from "newrelic"; + +newrelic.setTransactionName("foo"); // $ExpectType void + +const trans = newrelic.getTransaction(); +trans.ignore(); // $ExpectType void +trans.end(); // $ExpectType void +trans.end(() => { }); // $ExpectType void + +newrelic.setDispatcher("foo"); // $ExpectType void +newrelic.setDispatcher("foo", "42"); // $ExpectType void + +newrelic.setControllerName("foo", "GET"); // $ExpectType void + +newrelic.addCustomAttribute("foo", "bar"); // $ExpectType void +newrelic.addCustomAttributes({ foo: "bar", baz: "bang" }); // $ExpectType void + +newrelic.setIgnoreTransaction(true); // $ExpectType void + +newrelic.noticeError(Error("Oh no!")); // $ExpectType void +newrelic.noticeError(Error("Oh no!"), { foo: "bar" }); // $ExpectType void + +newrelic.addNamingRule("/user/customers/all/.*", "/user/customers/all"); // $ExpectType void +newrelic.addNamingRule(/\/user\/customers\/.*/, "/user/customers/:customer"); // $ExpectType void + +newrelic.addIgnoringRule("^/items/[0-9]+$"); // $ExpectType void +newrelic.addIgnoringRule(/^[0-9]+$/); // $ExpectType void + +newrelic.getBrowserTimingHeader(); // $ExpectType string + +const wrappedFn = newrelic.createTracer("foo", (x: number) => { + return x * x; +}); +const wrappedResult: number = wrappedFn(42); + +newrelic.startWebTransaction('/some/url/path', () => { + const transaction = newrelic.getTransaction(); + Promise.all([]); + setTimeout(() => { + // do some work + transaction.end(); + }, 100); +}); + +newrelic.startBackgroundTransaction('Red October', (foo) => foo); // $ExpectType any +newrelic.startBackgroundTransaction('Red October', 'Subs', () => { + const transaction = newrelic.getTransaction(); + setTimeout(() => { + // do some work + transaction.end(); + }, 100); +}); + +newrelic.endTransaction(); // $ExpectType void + +newrelic.recordMetric("foo", 42); // $ExpectType void +newrelic.recordMetric("foo", { + count: 10, + total: 42, + min: 1, + max: 17, + sumOfSquares: 60, +}); + +newrelic.incrementMetric("foo"); // $ExpectType void +newrelic.incrementMetric("foo", 42); // $ExpectType void + +newrelic.recordCustomEvent("my_event", { + foo: true, + bar: 42, + baz: "don't panic", +}); + +newrelic.instrument("foo", () => { }); // $ExpectType void +newrelic.instrumentDatastore("foo", () => { }, (err: Error) => { }); +newrelic.instrumentWebframework({ + moduleName: "foo", + onRequire: () => { }, +}); +newrelic.instrumentMessages({ + moduleName: "foo", + onRequire: () => { }, + onError: (err) => { + const error: Error = err; + }, +}); + +newrelic.shutdown(); // $ExpectType void +newrelic.shutdown({ collectPendingData: true }); +newrelic.shutdown({ timeout: 3000 }); +newrelic.shutdown({ collectPendingData: true, timeout: 3000 }); +newrelic.shutdown({ collectPendingData: true, timeout: 3000 }, (err) => { + const error: Error | undefined = err; +}); +newrelic.shutdown((err) => { + const error: Error | undefined = err; +}); diff --git a/types/newrelic/tsconfig.json b/types/newrelic/tsconfig.json new file mode 100644 index 0000000000..c255488fde --- /dev/null +++ b/types/newrelic/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "newrelic-tests.ts" + ] +} diff --git a/types/newrelic/tslint.json b/types/newrelic/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/newrelic/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" }