Skip to content

Commit

Permalink
Moved everything to README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
kjin committed Feb 25, 2017
1 parent 532e371 commit 9b0c91a
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 306 deletions.
223 changes: 163 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,82 +137,180 @@ The trace configuration additionally exposes the `samplingRate` option which set

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 listed above (`express`, `hapi`, and `restify`), 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.
For any of the web frameworks listed above (`express`, `hapi`, `koa` and `restify`), 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.

The API is exposed by the `agent` returned by a call to `start`:
### Accessing the API

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

For child spans, you can either use the `startSpan` and `endSpan` API, or use the `runInSpan` function that uses a callback-style. For root spans, you must use `runInRootSpan`.

### Start & end

To start a new child span, use `agent.startSpan`. Each span requires a name, and you can optionally specify labels.
Calling the `start` function returns an instance of `TraceApi`, which provides an interface for tracing:

```javascript
var span = agent.startSpan('name', {label: 'value'});
var traceApi = require('@google/cloud-trace').start();
```

Once your work is complete, you can end a child span with `agent.endSpan`. You can again optionally associate labels with the span:
It can also be retrieved by subsequent calls to `get` elsewhere:

```javascript
agent.endSpan(span, {label2: 'value'});
// after start() is called
var traceApi = require('@google/cloud-trace').get();
```

Note that the labels parameter must be an object. Other types (e.g. string) will be silently ignored.
The object returned by both of these calls is guaranteed to have the surface described below, even if the agent is disabled.

### The `TraceApi` Object

A `TraceApi` instance, in short, provides functions that facilitate the following:

- Creating trace spans and add labels to them.
- Getting information about how the trace agent was configured in the current application.
- Parsing and serializing trace contexts to support distributed tracing between microservices.
- Binding callbacks and event emitters in order to propagate trace contexts across asynchronous boundaries.

In addition to the above, `TraceApi` also provides a number of well-known label keys and constants through its `labels` and `constants` fields respectively.

#### Trace Spans

These functions provide the capability to create trace spans, add labels to them, and close them. `transaction` and `childSpan` are instances of `Transaction` and `ChildSpan`, respectively.

* `TraceApi#api.runInRootSpan(options, fn)`
* `options`: [`TraceOptions`](#trace-span-options)
* `fn`: `function(?Span): any`
* Returns `any`
* Attempts to create a root span, run the given callback, and pass it a `Span` object if the root span was successfuly created. Otherwise, the given function is run with `null` as an argument. This may be for one of two reasons:
* The trace policy, as specified by the user-given configuration, disallows a root span from being created under the current circumstances.
* The trace agent is disabled, either because it wasn't started at all, started in disabled mode, or started in an environment where the GCP project ID could not be obtained.
* `TraceApi#createChildSpan(options)`
* `options`: [`TraceOptions`](#trace-span-options)
* Returns `?Span`
* Attempts to create a child span, and returns a `Span` object if this is successful. Otherwise, it returns `null`. This may be for one of several reasons:
* A root span wasn't created beforehand because an earlier call to `runInRootSpan` didn't generate one.
* A root span wasn't created beforehand because `runInRootSpan` was not called at all.
* A root span was created beforehand, but context was lost between then and now.
* **Note:** Child spans are always associated with a parent root span, and must always be created within the context of its parent. See [`Context Propagation`](#context-propagation) for details on properly propagating root span context.
* `Span#addLabel(key, value)`
* `key`: `string`
* `value`: `any`
* Add a label to the span associated with the calling object. If the value is not a string, it will be stringified with `util.inspect`.
* **Note:** Keys and values may be truncated according to the user's configuration and limits set on the Stackdriver Trace API. Keys must be less than 128 bytes, while values must be less than 16 kilobytes, as specified in the [Stackdriver Trace docs][stackdriver-trace-span]. The user may specify a smaller limit on value size through the `maximumLabelValueSize` configuration field.
* `Span#endSpan()`
* Ends the span associated with the calling object. This function should only be called once.

##### Trace Span Options

### Run in span
Some functions above accept a `TraceOptions` object, which has the following fields:

* `name`: `string`
* Required
* The name that should be given to the newly created span.
* `traceContext`: `string`
* Optional for root spans, ignored for child spans
* A serialized trace context. If the module being traced is a web framework,
the plugin that patches this module should attempt to extract this from an
incoming request header and set this field; omitting this field may cause
trace spans that correspond to a single request across several services in a
distributed environment (e.g. microservices) to appear disassociated with
one another.
See also [Cross-Service Trace Contexts](#cross-service-trace-contexts).
* `url`: `string`
* Optional for root spans, ignored for child spans
* The URL of the incoming request. This only applies if the module being
traced is a web framework. If given, a label will automatically be created
for the new span for the url (under the key `url`). This field will also be
compared against the trace agent's URL filtering policy to check whether a
span should be created.
* Plugin developers should favor populating this field over using
`Span#addLabel` to add the `url`, as adding the url here bypasses user-set
label limits.
* `skipFrames`: `number`
* Optional; defaults to `0`
* Trace spans include the call stack at the moment of creation as part of the
information gathered. The call stack may include undesirable frames such as
frames within the plugin itself. This field specifies the number of stack
frames to skip when writing the call stack to the trace span. Frames within
the trace agent implementation are automatically skipped.

#### Trace Agent Configuration

* `TraceApi#enhancedDatabaseReportingEnabled()`
* Returns `boolean`
* Returns whether the trace agent was started with an enhanced level of reporting. See the [configuration][config-js] object definition for more details.

#### Cross-Service Trace Contexts

The Trace Agent supports distributed tracing, so that in supported web frameworks, incoming requests that are known to come from other services that are also integrated with Stackdriver Trace (through the ['x-cloud-trace-context'][stackdriver-trace-faq] field in request headers) should build spans that are aware of the information serialized in this field, known as the trace context. (For more information, see the [Dapper][dapper-paper] paper describing the distributed tracing system.)

It is up to plugin developers to extract serialized trace context from incoming requests and propagate it in outgoing requests. The Plugin API accepts the serialized trace context as an [option](#trace-span-options) when creating new trace spans.

The string `'x-cloud-trace-context'` is provided as `api.constants.TRACE_CONTEXT_HEADER_NAME`.

* `Span#getTraceContext()`
* Returns `string`
* Gets the trace context serialized as a string.

#### Context Propagation

These functions help provide context propagation for root spans. Context should be propagated anywhere control is yielded to the user; this is either through a callback or an emitter. This will enable child spans to be associated with the correct root span.

* `api.bind(fn)`
* `fn`: `function`
* Returns `function` (same signature as `fn`)
* Binds the given function to the current context.

* `api.bindEmitter(emitter)`
* `emitter`: `EventEmitter`
* Binds any event handlers subsequently attached to the given event emitter to the current context.

## 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 such as `express`, `mongodb`, and `http`, 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 patched. It will be passed two arguments: the object exported from the file, and an instance of `TraceApi`.
* `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.

If `patch` is supplied, then `unpatch` will be called if the agent must be disabled for any reason. This does not hold true for `intercept`: instead, the module exports for the original file will automatically be set to its original, unintercepted value.

In addition, `patch` and `intercept` have overlapping functionality.

For these reasons, plugins should not implement `patch` and/or `unpatch` alongside `intercept`. We strongly encourage plugin developers to implement patching and unpatching methods, using `intercept` only when needed.

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 documented in detail
// in the "Custom Tracing API" section above.
//
}

`agent.runInSpan` takes a function to execute inside a custom child span with the given name. The function may be synchronous or asynchronous. If it is asynchronous, it must accept a 'endSpan' function as an argument that should be called once the asynchronous work has completed.

```javascript
agent.runInSpan('name', {label: 'value'}, function() {
doSynchronousWork();
});

agent.runInSpan('name', {label: 'value'}, function(endSpan) {
doAsyncWork(function(result) {
processResult(result);
endSpan({label2: 'value'});
});
});
function patchModuleRoot3x(expressModule, traceApi) {
// ...
}

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

### Run in root span

`agent.runInRootSpan` behaves similarly to `agent.runInSpan`, except that the function is run within a root span.
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.

```javascript
agent.runInRootSpan('name', {label: 'value'}, function() {
// You can record child spans in here
doSynchronousWork();
});
agent.runInRootSpan('name', {label: 'value'}, function(endSpan) {
// You can record child spans in here
doAsyncWork(function(result) {
processResult(result);
endSpan({label2: 'value'});
});
});
```
We recommend using [`shimmer`][shimmer] to modify function properties on objects.

### Changing trace properties

It is possible to rename and add labels to current trace. This can be used to give it a more meaningful name or add additional metadata.

By default we use the name of the express (or hapi/restify) route as the transaction name, but it can be changed using `agent.setTransactionName`:

```javascript
agent.setTransactionName('new name');
```

You can add additional labels using `agent.addTransactionLabel`:

```javascript
agent.addTransactionLabel('label', 'value');
```
Please refer to the [built-in plugins][builtin-plugins] for more comprehensive examples.

## Contributing changes

Expand All @@ -226,6 +324,11 @@ You can add additional labels using `agent.addTransactionLabel`:
[gcloud-sdk]: https://cloud.google.com/sdk/gcloud/
[app-default-credentials]: https://developers.google.com/identity/protocols/application-default-credentials
[service-account]: https://console.developers.google.com/apis/credentials/serviceaccountkey
[stackdriver-trace-faq]: https://cloud.google.com/trace/docs/faq
[stackdriver-trace-span]: https://cloud.google.com/trace/api/reference/rest/v1/projects.traces#TraceSpan
[dapper-paper]: https://research.google.com/pubs/pub36356.html
[shimmer]: https://github.com/othiym23/shimmer
[builtin-plugins]: https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/tree/master/src/plugins
[npm-image]: https://badge.fury.io/js/%40google%2Fcloud-trace.svg
[npm-url]: https://npmjs.org/package/@google/cloud-trace
[travis-image]: https://travis-ci.org/GoogleCloudPlatform/cloud-trace-nodejs.svg?branch=master
Expand Down
Loading

0 comments on commit 9b0c91a

Please sign in to comment.