Skip to content

Commit

Permalink
Trace API and Plugin Developer Docs (#427)
Browse files Browse the repository at this point in the history
PR-URL: #427
  • Loading branch information
kjin authored Feb 28, 2017
1 parent 2a374fd commit d030be1
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 102 deletions.
69 changes: 54 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,19 @@ This is the trace list that shows a sampling of the incoming requests your appli
## What gets traced

The trace agent can do automatic tracing of the following web frameworks:
* [express](https://www.npmjs.com/package/express) version 4
* [gRPC](https://www.npmjs.com/package/grpc) version 1
* [hapi](https://www.npmjs.com/package/hapi) versions 8 - 16
* [koa](https://www.npmjs.com/package/koa) version 1
* [restify](https://www.npmjs.com/package/restify) versions 3 - 4
* [express](https://www.npmjs.com/package/express) (version 4)
* [gRPC](https://www.npmjs.com/package/grpc) server (version 1)
* [hapi](https://www.npmjs.com/package/hapi) (versions 8 - 16)
* [koa](https://www.npmjs.com/package/koa) (version 1)
* [restify](https://www.npmjs.com/package/restify) (versions 3 - 4)

The agent will also automatic trace of the following kinds of RPCs:
* Outbound HTTP requests through the `http` and `https` core modules
* [gRPC](https://www.npmjs.com/package/grpc) version 1
* [MongoDB-core](https://www.npmjs.com/package/mongodb-core) version 1
* [Mongoose](https://www.npmjs.com/package/mongoose) version 4
* [Redis](https://www.npmjs.com/package/redis) versions 0.12 - 2
* [MySQL](https://www.npmjs.com/package/mysql) version ^2.9
* [gRPC](https://www.npmjs.com/package/grpc) client (version 1)
* [MongoDB-core](https://www.npmjs.com/package/mongodb-core) (version 1)
* [Mongoose](https://www.npmjs.com/package/mongoose) (version 4)
* [Redis](https://www.npmjs.com/package/redis) (versions 0.12 - 2)
* [MySQL](https://www.npmjs.com/package/mysql) (version ^2.9)

You can use the [Custom Tracing API](#custom-tracing-api) to trace other processes in your application.

Expand All @@ -126,16 +126,55 @@ The trace agent can be configured by passing a configurations object to the agen

One configuration option of note is `enhancedDatabaseReporting`. Setting this option to `true` will cause database operations for redis and MongoDB to record query summaries and results as labels on reported trace spans.

## Disabling the trace agent

The trace agent can be turned off by either setting the `GCLOUD_TRACE_DISABLE` environment variable or specifying `enabled: false` in your configuration file.

## Trace batching and sampling
### Trace batching and sampling

The aggregation of trace spans before publishing can be configured using the `flushDelaySeconds` and `bufferSize` [options](config.js). The spans recorded for each incoming requests are placed in a buffer after the request has completed. Spans will be published to the UI in batch when the spans from `bufferSize` requests have been queued in the buffer or after `flushDelaySeconds` have passed since the last publish, whichever comes first.

The trace configuration additionally exposes the `samplingRate` option which sets an upper bound on the number of traced requests captured per second. Some Google Cloud environments may override this sampling policy.

### Tracing Additional Modules

In addition to the modules listed [above](#what-gets-traced), the trace agent can be configured to trace additional modules through the use of *plugins*. To load an additional plugin, specify it in the agent's configuration:

```javascript
require('@google/cloud-trace').start({
plugins: {
// You may use a package name or absolute path to the file.
'my-module': '@google/cloud-trace-plugin-my-module',
'another-module': path.join(__dirname, 'path/to/my-custom-plugins/plugin-another-module.js')
}
});
```

This list of plugins will be merged with the list of built-in plugins, which will be loaded by the plugin loader. Each plugin is only loaded when the module that it patches is loaded; in other words, there is no computational overhead for listing plugins for unused modules.

To create a plugin for a module, please see the [Plugin Developer Guide](./plugin-guide.md).

## Custom Tracing API

The custom tracing API can be used to add custom spans to trace. A *span* is a particular unit of work within a trace, such as an RPC request. Spans may be nested; the outermost span is called a *root span*, even if there are no nested child spans. Root spans typically correspond to incoming requests, while *child spans* typically correspond to outgoing requests, or other work that is triggered in response to incoming requests.

For any of the web frameworks for which we provide [built-in plugins](#what-gets-traced), a root span is automatically started whenever an incoming request is received (in other words, all middleware already runs within a root span). If you wish to record a span outside of any of these frameworks, any traced code must run within a root span that you create yourself.

### Accessing the API

Calling the `start` function returns an instance of `TraceApi`, which provides an interface for tracing:

```javascript
var traceApi = require('@google/cloud-trace').start();
```

It can also be retrieved by subsequent calls to `get` elsewhere:

```javascript
// after start() is called
var traceApi = require('@google/cloud-trace').get();
```

A `TraceApi` object is guaranteed to be returned by both of these calls, even if the agent is disabled.

A fully detailed overview of the `TraceApi` object is available [here](./trace-api.md).

## Contributing changes

* See [CONTRIBUTING.md](CONTRIBUTING.md)
Expand Down
55 changes: 55 additions & 0 deletions doc/plugin-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Plugin Developer Guide

The trace agent is driven by a set of plugins that describe how to patch a module to generate trace spans when that module is used. We provide plugins for some well-known modules (see [What gets traced](../README.md#what-gets-traced)), and provide a means for developers to create their own.

A plugin consists of a set of *patch objects*. A patch object gives information about how a file in a module should be patched in order to create trace spans.

Each patch object can contain the following fields:

* `file`: The path to the file whose exports should be patched, relative to the root of the module. You can specify an empty string, or omit this field entirely, to specify the export of the module itself.
* `versions`: A `semver` expression which will be used to control whether the specified file will be patched based on the module version; the patch will only be applied if the loaded module version satisfies this expression. This might be useful if your plugin only works on some versions of a module, or if you are patching internal mechanisms that are specific to a certain range of versions. If omitted, all versions of the module will be patched.
* `patch`: A function describing how the module exports for a given file should be modified. It will be passed two arguments: the object exported from the file, and an instance of [`TraceApi`](./trace-api.md).
* `intercept`: A function describing how the module exports for a file should be replaced with a new object. It accepts the same arguments as `patch`, but unlike `patch`, it should return the object that will be treated as the replacement value for `module.exports` (hence the name `intercept`).
* `unpatch`: A function describing how the module exports for a given file should be unpatched. This should generally mirror the logic in `patch`; for example, if `patch` wraps a method, `unpatch` should unwrap it.

Your module should either implement `patch` (strongly encouraged) or `intercept`, but not both. `patch` and `intercept` have overlapping functionality, so the plugin loader will throw an error if it encounters a plugin where both are implemented.

If the agent must be disabled for any reason, it will attempt to undo any patches or intercepts, as follows:
* If a module is patched, then `unpatch` will be called if it exists in that module's plugin. We strongly recommend implementing `unpatch`.
* If a module is intercepted, then all future `require`s of that module will automatically yield the original version of that module.


A plugin simply exports a list of patch objects.

For example, here's what a plugin for `express` might export:

```js
// Patches express 3.x/4.x.
// Only the patch function corresponding to the version of express will be
// called.

function patchModuleRoot4x(expressModule, traceApi) {
// Patch expressModule using the traceApi object here.
// expressModule is the object retrieved with require('express').
// traceApi exposes methods to facilitate tracing, and is the same as the
// object returned by a call to require('@google/cloud-trace').start().
}

function patchModuleRoot3x(expressModule, traceApi) {
// ...
}

module.exports = [
{ file: '', versions: '4.x', patch: patchModuleRoot4x },
{ file: '', versions: '3.x', patch: patchModuleRoot3x }
];
```

In most cases, it should be sufficient to patch just the root (public exports) of the module, in which case the `file` field can set to `''` or omitted. Based on how the module being patched is implemented, however, it may be necessary to patch other parts of the module as well.

We recommend using [`shimmer`][shimmer] to modify function properties on objects.

Please refer to the [built-in plugins][builtin-plugins] for more comprehensive examples.

[shimmer]: https://github.com/othiym23/shimmer
[builtin-plugins]: https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/tree/master/src/plugins
Loading

0 comments on commit d030be1

Please sign in to comment.