Skip to content

Commit

Permalink
Merge 0.20 to master (#242)
Browse files Browse the repository at this point in the history
* Add build script command

* Adds schema and typescript files to npmignore

* Add tagOverrides and contextObjects (#210)

* Add tagOverrides and contextObjects

Also updated documentation. Yes, it's 1 commit. Yes, I'm a terrible person for doing that.

* Add contextObject + formatting

* Fix CORS error when using library with browserify

Fixes an obscure problem when trying to use the ApplicationInsights node.js library with a browserify application.

* Add missing docs (#218)

* Update cross component dependency type (#219)

* migrate from typings to @types (#221)

* Add sampling support (#217)

* Add sampling support

* Fix test failure

* Update readme

* Fix race condition in readme example

* Fix build error from typings changes

* update package install and build scripts (#224)

* update package install and build scripts

* Adds a prepare script to replace the prepublish script for `npm install`
in npm@5+.
* Built artifacts from tsc are aggregated and stored in ./out for easier
workspace management.
* New test case which isn't transpiled by tsc to test some scenarios.
Includes a test for loading the transpiled module.

PR-URL:
Reviewed-By:
Reviewed-By:

* fixup! update package install and build scripts

* edits to comments in applicationinsights.ts (#223)

* edits to comments

* fixup! edits to comments

* Use autogenerated schemas (#228)

* Update BOND schema, update generated typescript with new comment generation

* Update autogenerated schemas. Migrate project to only use autogenerated schema classes

* Fix build errors

* Address PR feedback

* Add missing metric

* Change metric names to be in line with .NET SDK for common metrics

* Fix broken percentage units in performance counters

* pick up generated Declaration submodule (#232)

* Set the host app version to the context tags. #183 (#233)

* Sets version to the context tags

* No Readme changes

* Correct unit test

* Update Client.ts

Typo on the comment solved and switched the method name from setVersion to overrideApplicationVersion.

* Update Client.tests.ts

I apologize for the inconvenience, unit test corrected.

* Updating how cross-application correlations are tracked (#231)

* Updating how cross-application correlations are tracked

Instead of using a hash of the instrumentation key we now use the appId, matching the .NET sdk.
We also use different headers to match the .NET sdk.

* Updating to only issue appId requests once per ikey

* Exposing profileQueryEndpoint property

Allows for the appId query target to be configured separately to the telemetry endpoint.
It may be specified either by the APPINSIGHTS_PROFILE_QUERY_ENDPOINT environment variable,
or explicitly via client.config.profileQueryEndpoint. Note that it must be set synchronously
with the creation of the Config otherwise the value will not be used.

* Allowing appId lookups to be cancelled if a new endpoint is specified

* Adding operationId to outbound HTTP headers

* Provide access to contracts (#234)

* Provide access to contracts

* Test for access to contracts

* Change access to Contracts from Client.ts to applicationinsights.ts

* Fix baseTypes (#236)

* update README

* address Alex's feedback

* Fixing CorrelationId

There was a typo which lead to the `correlationIdPrefix` being null.

* Adding diagnostic-channel (#235)

* Adding diagnostic-channel

By using diagnostic-channel and diagnostic-channel-publishers, we can support context tracking through third-party libraries as well as getting additional telemetry for those dependencies.

* Fixing cyclical reference and supporting multiple AI clients in diagnostic-channel subscribers

* Updating readme with diagnostic-channel information

* Update Readme (#238)

* Enable automatic correlation by default (#239)

* Include typescript definitions in NPM package (#240)

* Update package.json to 0.20 (#241)
  • Loading branch information
OsvaldoRosado authored May 10, 2017
1 parent 2116076 commit e1ae9ee
Show file tree
Hide file tree
Showing 86 changed files with 1,851 additions and 3,183 deletions.
10 changes: 2 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
# Ignore build files
obj
*.d.ts
*.tgz
package-version
out/
obj/

# Except imported Declarations and Typings
!/Declarations/**
!/typings/**

# Ignore Node files
node_modules/**
Expand All @@ -27,6 +24,3 @@ node_modules/**
# Ignore log files
npm-debug.log


*.js.map
*.js
10 changes: 8 additions & 2 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ Tests

# Don't publish source files that aren't needed in package
*.ts
!*.d.ts
*.js.map
*.d.ts
.travis.yml
.ntvs_analysis.dat

Declarations
/Declarations

Schema

typings
tsconfig.json
typings.json
32 changes: 16 additions & 16 deletions AutoCollection/ClientRequestParser.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
///<reference path="..\typings\globals\node\index.d.ts" />

import http = require("http");
import https = require("https");
import url = require("url");

import ContractsModule = require("../Library/Contracts");
import Contracts = require("../Declarations/Contracts");
import Client = require("../Library/Client");
import Logging = require("../Library/Logging");
import Util = require("../Library/Util");
import RequestResponseHeaders = require("../Library/RequestResponseHeaders");
import RequestParser = require("./RequestParser");
import CorrelationIdManager = require("../Library/CorrelationIdManager");

/**
* Helper class to read data from the requst/response objects and convert them into the telemetry contract
*/
class ClientRequestParser extends RequestParser {
private targetIKeyHash: string;
private correlationId: string;

constructor(requestOptions: string | http.RequestOptions | https.RequestOptions, request: http.ClientRequest) {
super();
Expand All @@ -40,28 +39,29 @@ class ClientRequestParser extends RequestParser {
*/
public onResponse(response: http.ClientResponse, properties?: { [key: string]: string }) {
this._setStatus(response.statusCode, undefined, properties);
this.targetIKeyHash =
response.headers && response.headers[RequestResponseHeaders.targetInstrumentationKeyHeader];
this.correlationId = Util.getCorrelationContextTarget(response, RequestResponseHeaders.requestContextTargetKey);
}

/**
* Gets a dependency data contract object for a completed ClientRequest.
*/
public getDependencyData(): ContractsModule.Contracts.Data<ContractsModule.Contracts.RemoteDependencyData> {
public getDependencyData(): Contracts.Data<Contracts.RemoteDependencyData> {
let urlObject = url.parse(this.url);
urlObject.search = undefined;
urlObject.hash = undefined;
let dependencyName = this.method.toUpperCase() + " " + urlObject.pathname;

let remoteDependency = new ContractsModule.Contracts.RemoteDependencyData();
remoteDependency.type = ContractsModule.Contracts.RemoteDependencyDataConstants.TYPE_HTTP;
let remoteDependency = new Contracts.RemoteDependencyData();
remoteDependency.type = Contracts.RemoteDependencyDataConstants.TYPE_HTTP;

if (this.targetIKeyHash) {
remoteDependency.type = "ApplicationInsights";
remoteDependency.target = urlObject.hostname + " | " + this.targetIKeyHash;
remoteDependency.target = urlObject.hostname;
if (this.correlationId) {
remoteDependency.type = Contracts.RemoteDependencyDataConstants.TYPE_AI;
if (this.correlationId !== CorrelationIdManager.correlationIdPrefix) {
remoteDependency.target = urlObject.hostname + " | " + this.correlationId;
}
} else {
remoteDependency.type = ContractsModule.Contracts.RemoteDependencyDataConstants.TYPE_HTTP;
remoteDependency.target = urlObject.hostname;
remoteDependency.type = Contracts.RemoteDependencyDataConstants.TYPE_HTTP;
}

remoteDependency.name = dependencyName;
Expand All @@ -71,8 +71,8 @@ class ClientRequestParser extends RequestParser {
remoteDependency.resultCode = this.statusCode ? this.statusCode.toString() : null;
remoteDependency.properties = this.properties || {};

let data = new ContractsModule.Contracts.Data<ContractsModule.Contracts.RemoteDependencyData>();
data.baseType = "Microsoft.ApplicationInsights.RemoteDependencyData";
let data = new Contracts.Data<Contracts.RemoteDependencyData>();
data.baseType = Contracts.DataTypes.REMOTE_DEPENDENCY;
data.baseData = remoteDependency;

return data;
Expand Down
41 changes: 31 additions & 10 deletions AutoCollection/ClientRequests.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
///<reference path="..\typings\globals\node\index.d.ts" />

import http = require("http");
import https = require("https");
import url = require("url");

import ContractsModule = require("../Library/Contracts");
import Client = require("../Library/Client");
import Logging = require("../Library/Logging");
import Util = require("../Library/Util");
import RequestResponseHeaders = require("../Library/RequestResponseHeaders");
import ClientRequestParser = require("./ClientRequestParser");
import { CorrelationContextManager, CorrelationContext } from "./CorrelationContextManager";

import {enable as enableMongodb} from "./diagnostic-channel/mongodb.sub";
import {enable as enableMysql} from "./diagnostic-channel/mysql.sub";
import {enable as enableRedis} from "./diagnostic-channel/redis.sub";

import "./diagnostic-channel/initialization";

class AutoCollectClientRequests {
public static disableCollectionRequestOption = 'disableAppInsightsAutoCollection';
Expand All @@ -34,6 +38,9 @@ class AutoCollectClientRequests {
if (this._isEnabled && !this._isInitialized) {
this._initialize();
}
enableMongodb(isEnabled, this._client);
enableMysql(isEnabled, this._client);
enableRedis(isEnabled, this._client);
}

public isInitialized() {
Expand Down Expand Up @@ -82,16 +89,30 @@ class AutoCollectClientRequests {

let requestParser = new ClientRequestParser(requestOptions, request);

// Add the source ikey hash to the request headers, if a value was not already provided.
// Add the source correlationId to the request headers, if a value was not already provided.
// The getHeader/setHeader methods aren't available on very old Node versions, and
// are not included in the v0.10 type declarations currently used. So check if the
// methods exist before invoking them.
if (client.config && client.config.instrumentationKeyHash &&
Util.canIncludeCorrelationHeader(client, requestParser.getUrl()) &&
request['getHeader'] && request['setHeader'] &&
!request['getHeader'](RequestResponseHeaders.sourceInstrumentationKeyHeader)) {
request['setHeader'](RequestResponseHeaders.sourceInstrumentationKeyHeader,
client.config.instrumentationKeyHash);
if (Util.canIncludeCorrelationHeader(client, requestParser.getUrl()) &&
request['getHeader'] && request['setHeader']) {
if (client.config && client.config.correlationId) {
const correlationHeader = request['getHeader'](RequestResponseHeaders.requestContextHeader);
if (correlationHeader) {
const components = correlationHeader.split(",");
const key = `${RequestResponseHeaders.requestContextSourceKey}=`;
if (!components.some((value) => value.substring(0,key.length) === key)) {
request['setHeader'](RequestResponseHeaders.requestContextHeader, `${correlationHeader},${RequestResponseHeaders.requestContextSourceKey}=${client.config.correlationId}`);
}
} else {
request['setHeader'](RequestResponseHeaders.requestContextHeader, `${RequestResponseHeaders.requestContextSourceKey}=${client.config.correlationId}`);
}
}

const currentContext = CorrelationContextManager.getCurrentContext();
if (currentContext && currentContext.operation) {
request['setHeader'](RequestResponseHeaders.parentIdHeader, currentContext.operation.id);
request['setHeader'](RequestResponseHeaders.rootIdHeader, currentContext.operation.parentId);
}
}

// Collect dependency telemetry about the request when it finishes.
Expand Down
8 changes: 7 additions & 1 deletion AutoCollection/Console.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import Client = require("../Library/Client");
import Logging = require("../Library/Logging");

import {enable as enableConsole} from "./diagnostic-channel/console.sub";
import {enable as enableBunyan} from "./diagnostic-channel/bunyan.sub";

import "./diagnostic-channel/initialization";

class AutoCollectConsole {
public static originalMethods: {[name: string]: (message?: any, ...optionalParams: any[]) => void};

Expand All @@ -20,7 +25,8 @@ class AutoCollectConsole {
}

public enable(isEnabled: boolean) {
// todo: investigate feasibility/utility of this; does it make sense to have a logging adapter in node?
enableConsole(isEnabled, this._client);
enableBunyan(isEnabled, this._client);
}

public isInitialized() {
Expand Down
43 changes: 11 additions & 32 deletions AutoCollection/CorrelationContextManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/// <reference path="..\node_modules\zone.js\dist\zone.js.d.ts" />
import http = require("http");
import Util = require("../Library/Util");

import {channel} from "diagnostic-channel";

export interface CorrelationContext {
operation: {
name: string;
Expand Down Expand Up @@ -99,9 +100,11 @@ export class CorrelationContextManager {
// Run patches for Zone.js
if (!this.hasEverEnabled) {
this.hasEverEnabled = true;
channel.addContextPreservation((cb) => {
return Zone.current.wrap(cb, "AI-ContextPreservation");
})
this.patchError();
this.patchTimers(["setTimeout", "setInterval"]);
this.patchRedis();
}

this.enabled = true;
Expand Down Expand Up @@ -135,33 +138,6 @@ export class CorrelationContextManager {
return req;
}

// A good example of patching a third party library to respect context.
// send_command is always used in this library to send data out.
// By overwriting the function to capture the callback provided to it,
// and wrapping that callback, we ensure that consumers of this library
// will have context persisted.
private static patchRedis() {
var redis = this.requireForPatch("redis");

if (redis && redis.RedisClient) {
var orig = redis.RedisClient.prototype.send_command;
redis.RedisClient.prototype.send_command = function() {
var args = Array.prototype.slice.call(arguments);
var lastArg = args[args.length - 1];

if (typeof lastArg === "function") {
args[args.length - 1] = Zone.current.wrap(lastArg, "AI.CCM.patchRedis");
} else if (lastArg instanceof Array && typeof lastArg[lastArg.length - 1] === "function") {
// The last argument can be an array!
var lastIndexLastArg = lastArg[lastArg.length - 1];
lastArg[lastArg.length - 1] = Zone.current.wrap(lastIndexLastArg, "AI.CCM.patchRedis");
}

return orig.apply(this, args);
};
}
}

// Zone.js breaks concatenation of timer return values.
// This fixes that.
private static patchTimers(methodNames: string[]) {
Expand Down Expand Up @@ -200,7 +176,7 @@ export class CorrelationContextManager {

// Zone.js will automatically create some hidden properties at read time.
// We need to proactively make those not enumerable as well as the currently visible properties
for(var i=0; i<props.length; i++) {
for(var i=0; i < props.length; i++) {
var propertyName = props[i];
var hiddenPropertyName = Zone['__symbol__'](propertyName);
Object.defineProperty(this, propertyName, { enumerable: false });
Expand All @@ -215,12 +191,15 @@ export class CorrelationContextManager {

// We need this loop to copy outer methods like Error.captureStackTrace
var props = Object.getOwnPropertyNames(orig);
for(var i=0; i<props.length; i++) {
for(var i=0; i < props.length; i++) {
var propertyName = props[i];
if (!AppInsightsAsyncCorrelatedErrorWrapper[propertyName]) {
Object.defineProperty(AppInsightsAsyncCorrelatedErrorWrapper, propertyName, Object.getOwnPropertyDescriptor(orig, propertyName));
}
}
global.Error = AppInsightsAsyncCorrelatedErrorWrapper;

// explicit cast to <any> required to avoid type error for captureStackTrace
// with latest node.d.ts (despite workaround above)
global.Error = <any>AppInsightsAsyncCorrelatedErrorWrapper;
}
}
16 changes: 7 additions & 9 deletions AutoCollection/Exceptions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
///<reference path="..\typings\globals\node\index.d.ts" />

import http = require("http");

import ContractsModule = require("../Library/Contracts");
import Contracts = require("../Declarations/Contracts");
import Client = require("../Library/Client");
import Sender = require("../Library/Sender");
import Queue = require("../Library/Channel");
Expand Down Expand Up @@ -69,23 +67,23 @@ class AutoCollectExceptions {
* @param properties additional properties
* @param measurements metrics associated with this event, displayed in Metrics Explorer on the portal. Defaults to empty.
*/
public static getExceptionData(error: Error, isHandled: boolean, properties?:{ [key: string]: string; }, measurements?:{ [key: string]: number; }) {
var exception = new ContractsModule.Contracts.ExceptionData();
public static getExceptionData(error: Error, isHandled: boolean, properties?:{ [key: string]: string; }, measurements?:{ [key: string]: number; }): Contracts.Data<Contracts.ExceptionData> {
var exception = new Contracts.ExceptionData();
exception.properties = properties;
exception.severityLevel = ContractsModule.Contracts.SeverityLevel.Error;
exception.severityLevel = Contracts.SeverityLevel.Error;
exception.measurements = measurements;
exception.exceptions = [];

var stack = error["stack"];
var exceptionDetails = new ContractsModule.Contracts.ExceptionDetails();
var exceptionDetails = new Contracts.ExceptionDetails();
exceptionDetails.message = error.message;
exceptionDetails.typeName = error.name;
exceptionDetails.parsedStack = this.parseStack(stack);
exceptionDetails.hasFullStack = Util.isArray(exceptionDetails.parsedStack) && exceptionDetails.parsedStack.length > 0;
exception.exceptions.push(exceptionDetails);

var data = new ContractsModule.Contracts.Data<ContractsModule.Contracts.ExceptionData>();
data.baseType = "Microsoft.ApplicationInsights.ExceptionData";
var data = new Contracts.Data<Contracts.ExceptionData>();
data.baseType = Contracts.DataTypes.EXCEPTION;
data.baseData = exception;
return data;
}
Expand Down
Loading

0 comments on commit e1ae9ee

Please sign in to comment.