Repository: microsoft/ApplicationInsights-node.js
Pull request #1080 (2023-02-18 00:12:57)

[Beta] Refactoring of Metric handler for more complete data


Patch:
diff --git a/src/logs/logHandler.ts b/src/logs/logHandler.ts
index c8ad3b5..b9ce1ad 100644
--- a/src/logs/logHandler.ts
+++ b/src/logs/logHandler.ts
@@ -34,7 +34,7 @@ import {
     Telemetry,
 } from "../declarations/contracts";
 import { Logger } from "../shared/logging";
-import { IMetricExceptionDimensions, IMetricTraceDimensions } from "../metrics/types";
+import { IStandardMetricBaseDimensions, IMetricTraceDimensions } from "../metrics/types";
 import { MetricHandler } from "../metrics/metricHandler";
 
 export class LogHandler {
@@ -119,9 +119,9 @@ export class LogHandler {
                 const traceDimensions: IMetricTraceDimensions = {
                     cloudRoleInstance: envelope.tags[KnownContextTagKeys.AiCloudRoleInstance],
                     cloudRoleName: envelope.tags[KnownContextTagKeys.AiCloudRole],
-                    traceSeverityLevel: baseData.severity,
+                    traceSeverityLevel: baseData.severity
                 };
-                this._metricHandler.countTrace(traceDimensions);
+                this._metricHandler.recordTrace(traceDimensions);
                 // Mark envelope as processed
                 const traceData: TraceTelemetry = (envelope.data as any).baseData;
                 traceData.properties = {
@@ -149,11 +149,11 @@ export class LogHandler {
                 this._config.getInstrumentationKey()
             );
             if (this._metricHandler?.getConfig().enableAutoCollectStandardMetrics) {
-                const exceptionDimensions: IMetricExceptionDimensions = {
+                const exceptionDimensions: IStandardMetricBaseDimensions = {
                     cloudRoleInstance: envelope.tags[KnownContextTagKeys.AiCloudRoleInstance],
-                    cloudRoleName: envelope.tags[KnownContextTagKeys.AiCloudRole],
+                    cloudRoleName: envelope.tags[KnownContextTagKeys.AiCloudRole]
                 };
-                this._metricHandler.countException(exceptionDimensions);
+                this._metricHandler.recordException(exceptionDimensions);
                 // Mark envelope as processed
                 const exceptionData: TelemetryExceptionData = (envelope.data as any).baseData;
                 exceptionData.properties = {
diff --git a/src/metrics/collection/azureHttpMetricsInstrumentation.ts b/src/metrics/collection/azureHttpMetricsInstrumentation.ts
deleted file mode 100644
index fd495ed..0000000
--- a/src/metrics/collection/azureHttpMetricsInstrumentation.ts
+++ /dev/null
@@ -1,369 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-import type * as http from "http";
-import type * as https from "https";
-export type Http = typeof http;
-import * as semver from "semver";
-import * as url from "url";
-import {
-    InstrumentationBase,
-    InstrumentationNodeModuleDefinition,
-    isWrapped,
-    safeExecuteInTheMiddle,
-} from "@opentelemetry/instrumentation";
-import { getRequestInfo, HttpInstrumentationConfig } from "@opentelemetry/instrumentation-http";
-import { SpanKind, TracerProvider, Histogram, MeterProvider, ValueType } from "@opentelemetry/api";
-import { SemanticAttributes } from "@opentelemetry/semantic-conventions";
-
-import { APPLICATION_INSIGHTS_SDK_VERSION } from "../../declarations/constants";
-import { IHttpStandardMetric, MetricName } from "../types";
-import { Logger } from "../../shared/logging";
-
-
-export class AzureHttpMetricsInstrumentation extends InstrumentationBase<Http> {
-    private _nodeVersion: string;
-    public totalRequestCount = 0;
-    public totalFailedRequestCount = 0;
-    public totalDependencyCount = 0;
-    public totalFailedDependencyCount = 0;
-    public intervalDependencyExecutionTime = 0;
-    public intervalRequestExecutionTime = 0;
-
-    private _httpServerDurationHistogram!: Histogram;
-    private _httpClientDurationHistogram!: Histogram;
-
-    constructor(config: HttpInstrumentationConfig = {}) {
-        super("AzureHttpMetricsInstrumentation", APPLICATION_INSIGHTS_SDK_VERSION, config);
-        this._nodeVersion = process.versions.node;
-        this._updateMetricInstruments();
-    }
-
-    public setTracerProvider(tracerProvider: TracerProvider) {
-        return;
-    }
-
-    public setMeterProvider(meterProvider: MeterProvider) {
-        super.setMeterProvider(meterProvider);
-        this._updateMetricInstruments();
-    }
-
-    protected _updateMetricInstruments() {
-        this._httpServerDurationHistogram = this.meter.createHistogram(
-            MetricName.REQUEST_DURATION,
-            { valueType: ValueType.DOUBLE }
-        );
-        this._httpClientDurationHistogram = this.meter.createHistogram(
-            MetricName.DEPENDENCY_DURATION,
-            { valueType: ValueType.DOUBLE }
-        );
-    }
-
-    public _getConfig(): HttpInstrumentationConfig {
-        return this._config;
-    }
-
-    /**
-     * Init method will be called when the plugin is constructed.
-     * It returns an `InstrumentationNodeModuleDefinition` which describes
-     *   the node module to be instrumented and patched.
-     * It may also return a list of `InstrumentationNodeModuleDefinition`s if
-     *   the plugin should patch multiple modules or versions.
-     */
-    protected init() {
-        return [this._getHttpDefinition(), this._getHttpsDefinition()];
-    }
-
-    private _httpRequestDone(metric: IHttpStandardMetric) {
-        // Done could be called multiple times, only process metric once
-        if (!metric.isProcessed) {
-            metric.isProcessed = true;
-            metric.attributes["_MS.IsAutocollected"] = "True";
-            const durationMs = Date.now() - metric.startTime;
-            let success = false;
-            const statusCode = parseInt(
-                String(metric.attributes[SemanticAttributes.HTTP_STATUS_CODE])
-            );
-            if (!isNaN(statusCode)) {
-                success = 0 < statusCode && statusCode < 500;
-            }
-            if (metric.spanKind === SpanKind.SERVER) {
-                this._httpServerDurationHistogram.record(durationMs, metric.attributes);
-                this.intervalRequestExecutionTime += durationMs;
-                if (!success) {
-                    this.totalFailedRequestCount++;
-                }
-                this.totalRequestCount++;
-            } else {
-                this._httpClientDurationHistogram.record(durationMs, metric.attributes);
-                this.intervalDependencyExecutionTime += durationMs;
-                if (!success) {
-                    this.totalFailedDependencyCount++;
-                }
-                this.totalDependencyCount++;
-            }
-        }
-    }
-
-    private _getHttpDefinition(): InstrumentationNodeModuleDefinition<typeof http> {
-        const httpsModule = new InstrumentationNodeModuleDefinition<typeof http>(
-            "http",
-            ["*"],
-            (moduleExports) => {
-                this._diag.debug(`Applying patch for http@${this._nodeVersion}`);
-                if (isWrapped(moduleExports.request)) {
-                    this._unwrap(moduleExports, "request");
-                }
-                this._wrap(moduleExports, "request", this._getPatchOutgoingRequestFunction("http"));
-                if (isWrapped(moduleExports.Server.prototype.emit)) {
-                    this._unwrap(moduleExports.Server.prototype, "emit");
-                }
-                this._wrap(
-                    moduleExports.Server.prototype,
-                    "emit",
-                    this._getPatchIncomingRequestFunction("http")
-                );
-                return moduleExports;
-            },
-            (moduleExports) => {
-                if (moduleExports === undefined) {
-                    return;
-                }
-                this._diag.debug(`Removing patch for http@${this._nodeVersion}`);
-
-                this._unwrap(moduleExports, "request");
-                this._unwrap(moduleExports.Server.prototype, "emit");
-            }
-        );
-        return httpsModule;
-    }
-
-    private _getHttpsDefinition(): InstrumentationNodeModuleDefinition<typeof https> {
-        const httpsModule = new InstrumentationNodeModuleDefinition<typeof https>(
-            "https",
-            ["*"],
-            (moduleExports) => {
-                this._diag.debug(`Applying patch for https@${this._nodeVersion}`);
-                if (isWrapped(moduleExports.request)) {
-                    this._unwrap(moduleExports, "request");
-                }
-                this._wrap(
-                    moduleExports,
-                    "request",
-                    this._getPatchOutgoingRequestFunction("https")
-                );
-                if (isWrapped(moduleExports.Server.prototype.emit)) {
-                    this._unwrap(moduleExports.Server.prototype, "emit");
-                }
-                this._wrap(
-                    moduleExports.Server.prototype,
-                    "emit",
-                    this._getPatchIncomingRequestFunction("https")
-                );
-                return moduleExports;
-            },
-            (moduleExports) => {
-                if (moduleExports === undefined) {
-                    return;
-                }
-                this._diag.debug(`Removing patch for https@${this._nodeVersion}`);
-
-                this._unwrap(moduleExports, "request");
-                this._unwrap(moduleExports.Server.prototype, "emit");
-            }
-        );
-        return httpsModule;
-    }
-
-    private _getPatchOutgoingRequestFunction(component: "http" | "https") {
-        return (
-            original: (...args: any[]) => http.ClientRequest
-        ): ((...args: any[]) => http.ClientRequest) =>
-            this._outgoingRequestFunction(component, original);
-    }
-
-    private _outgoingRequestFunction(
-        component: "http" | "https",
-        original: (...args: any[]) => http.ClientRequest
-    ): (...args: any[]) => http.ClientRequest {
-        // eslint-disable-next-line @typescript-eslint/no-this-alias
-        const instrumentation = this;
-        return function outgoingRequest(
-            this: unknown,
-            options: url.URL | http.RequestOptions | string,
-            ...args: unknown[]
-        ): http.ClientRequest {
-            /**
-             * Node 8's https module directly call the http one so to avoid creating
-             * 2 span for the same request we need to check that the protocol is correct
-             * See: https://github.com/nodejs/node/blob/v8.17.0/lib/https.js#L245
-             */
-            const { optionsParsed } = getRequestInfo(options);
-            if (
-                component === "http" &&
-                semver.lt(process.version, "9.0.0") &&
-                optionsParsed.protocol === "https:"
-            ) {
-                return original.apply(this, [options, ...args]);
-            }
-            if (
-                safeExecuteInTheMiddle(
-                    () => instrumentation._getConfig().ignoreOutgoingRequestHook?.(optionsParsed),
-                    (e: unknown) => {
-                        if (e) {
-                            instrumentation._diag.error(
-                                "caught ignoreOutgoingRequestHook error: ",
-                                e
-                            );
-                        }
-                    },
-                    true
-                )
-            ) {
-                return original.apply(this, [optionsParsed, ...args]);
-            }
-
-            const metric: IHttpStandardMetric = {
-                startTime: Date.now(),
-                isProcessed: false,
-                spanKind: SpanKind.CLIENT,
-                attributes: {},
-            };
-
-            metric.attributes[SemanticAttributes.HTTP_METHOD] = optionsParsed.method;
-            metric.attributes[SemanticAttributes.NET_PEER_NAME] = optionsParsed.hostname;
-
-            const request: http.ClientRequest = safeExecuteInTheMiddle(
-                () => original.apply(this, [options, ...args]),
-                (error) => {
-                    if (error) {
-                        throw error;
-                    }
-                }
-            );
-            request.prependListener(
-                "response",
-                (response: http.IncomingMessage & { aborted?: boolean; complete?: boolean }) => {
-                    Logger.getInstance().debug("outgoingRequest on response()");
-                    metric.attributes[SemanticAttributes.NET_PEER_PORT] = String(
-                        response.socket.remotePort
-                    );
-                    metric.attributes[SemanticAttributes.HTTP_STATUS_CODE] = String(
-                        response.statusCode
-                    );
-                    metric.attributes[SemanticAttributes.HTTP_FLAVOR] = response.httpVersion;
-
-                    response.on("end", () => {
-                        Logger.getInstance().debug("outgoingRequest on end()");
-                        instrumentation._httpRequestDone(metric);
-                    });
-                    response.on("error", (error: Error) => {
-                        Logger.getInstance().debug("outgoingRequest on response error()", error);
-                        instrumentation._httpRequestDone(metric);
-                    });
-                }
-            );
-            request.on("close", () => {
-                Logger.getInstance().debug("outgoingRequest on request close()");
-                if (!request.aborted) {
-                    instrumentation._httpRequestDone(metric);
-                }
-            });
-            request.on("error", (error: Error) => {
-                Logger.getInstance().debug("outgoingRequest on request error()");
-                instrumentation._httpRequestDone(metric);
-            });
-            return request;
-        };
-    }
-
-    private _getPatchIncomingRequestFunction(component: "http" | "https") {
-        return (
-            original: (event: string, ...args: unknown[]) => boolean
-        ): ((this: unknown, event: string, ...args: unknown[]) => boolean) =>
-            this._incomingRequestFunction(component, original);
-    }
-
-    private _incomingRequestFunction(
-        component: "http" | "https",
-        original: (event: string, ...args: unknown[]) => boolean
-    ) {
-        // eslint-disable-next-line @typescript-eslint/no-this-alias
-        const instrumentation = this;
-        return function incomingRequest(this: unknown, event: string, ...args: unknown[]): boolean {
-            // Only count request events
-            if (event !== "request") {
-                return original.apply(this, [event, ...args]);
-            }
-            const request = args[0] as http.IncomingMessage;
-            const response = args[1] as http.ServerResponse;
-
-            if (
-                safeExecuteInTheMiddle(
-                    () => instrumentation._getConfig().ignoreIncomingRequestHook?.(request),
-                    (e: unknown) => {
-                        if (e !== null) {
-                            instrumentation._diag.error(
-                                "caught ignoreIncomingRequestHook error: ",
-                                e
-                            );
-                        }
-                    },
-                    true
-                )
-            ) {
-                return original.apply(this, [event, ...args]);
-            }
-
-            const metric: IHttpStandardMetric = {
-                startTime: Date.now(),
-                spanKind: SpanKind.SERVER,
-                isProcessed: false,
-                attributes: {},
-            };
-
-            metric.attributes[SemanticAttributes.HTTP_SCHEME] = component;
-            metric.attributes[SemanticAttributes.HTTP_METHOD] = request.method || "GET";
-            metric.attributes[SemanticAttributes.HTTP_FLAVOR] = request.httpVersion;
-
-            const requestUrl = request.url ? url.parse(request.url) : null;
-            const hostname =
-                requestUrl?.hostname ||
-                requestUrl?.host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") ||
-                "localhost";
-            metric.attributes[SemanticAttributes.NET_HOST_NAME] = hostname;
-            metric.attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || "/";
-
-            const originalEnd = response.end;
-            response.end = function (this: http.ServerResponse, ..._args: any) {
-                response.end = originalEnd;
-                const returned = safeExecuteInTheMiddle(
-                    // eslint-disable-next-line prefer-rest-params
-                    () => response.end.apply(this, arguments as never),
-                    (error) => {
-                        if (error) {
-                            instrumentation._httpRequestDone(metric);
-                            throw error;
-                        }
-                    }
-                );
-                metric.attributes[SemanticAttributes.HTTP_STATUS_CODE] = String(
-                    response.statusCode
-                );
-                metric.attributes[SemanticAttributes.NET_HOST_PORT] = String(
-                    request.socket.localPort
-                );
-                instrumentation._httpRequestDone(metric);
-                return returned;
-            };
-            return safeExecuteInTheMiddle(
-                () => original.apply(this, [event, ...args]),
-                (error) => {
-                    if (error) {
-                        instrumentation._httpRequestDone(metric);
-                        throw error;
-                    }
-                }
-            );
-        };
-    }
-}
diff --git a/src/metrics/collection/dependencyMetrics.ts b/src/metrics/collection/dependencyMetrics.ts
index 8d4b972..ceb75a8 100644
--- a/src/metrics/collection/dependencyMetrics.ts
+++ b/src/metrics/collection/dependencyMetrics.ts
@@ -1,29 +1,34 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 import {
+    Histogram,
     Meter,
     ObservableCallback,
     ObservableGauge,
     ObservableResult,
     ValueType,
 } from "@opentelemetry/api";
-import { AzureHttpMetricsInstrumentation } from "./azureHttpMetricsInstrumentation";
 import { MetricName } from "../types";
 
 export class DependencyMetrics {
     private _meter: Meter;
-    private _httpMetrics: AzureHttpMetricsInstrumentation;
+    private _httpDurationHistogram: Histogram;
     private _dependencyFailureRateGauge: ObservableGauge;
     private _dependencyFailureRateGaugeCallback: ObservableCallback;
     private _dependencyRateGauge: ObservableGauge;
     private _dependencyRateGaugeCallback: ObservableCallback;
+    private _totalCount = 0;
+    private _totalFailedCount = 0;
+    private _intervalExecutionTime = 0;
     private _lastDependencyRate: { count: number; time: number; executionInterval: number };
     private _lastFailureDependencyRate: { count: number; time: number; executionInterval: number };
 
-    constructor(meter: Meter, httpMetrics: AzureHttpMetricsInstrumentation) {
+    constructor(meter: Meter) {
         this._meter = meter;
-        this._httpMetrics = httpMetrics;
-
+        this._httpDurationHistogram = this._meter.createHistogram(
+            MetricName.DEPENDENCY_DURATION,
+            { valueType: ValueType.DOUBLE }
+        );
         this._lastDependencyRate = { count: 0, time: 0, executionInterval: 0 };
         this._lastFailureDependencyRate = { count: 0, time: 0, executionInterval: 0 };
         this._dependencyRateGauge = this._meter.createObservableGauge(MetricName.DEPENDENCY_RATE, {
@@ -41,14 +46,14 @@ export class DependencyMetrics {
     public enable(isEnabled: boolean) {
         if (isEnabled) {
             this._lastDependencyRate = {
-                count: this._httpMetrics.totalDependencyCount,
+                count: this._totalCount,
                 time: +new Date(),
-                executionInterval: this._httpMetrics.intervalDependencyExecutionTime,
+                executionInterval: this._intervalExecutionTime,
             };
             this._lastFailureDependencyRate = {
-                count: this._httpMetrics.totalFailedRequestCount,
+                count: this._totalFailedCount,
                 time: +new Date(),
-                executionInterval: this._httpMetrics.intervalDependencyExecutionTime,
+                executionInterval: this._intervalExecutionTime,
             };
             this._dependencyFailureRateGauge.addCallback(this._dependencyFailureRateGaugeCallback);
             this._dependencyRateGauge.addCallback(this._dependencyRateGaugeCallback);
@@ -60,10 +65,24 @@ export class DependencyMetrics {
         }
     }
 
+    public getDurationHistogram(): Histogram {
+        return this._httpDurationHistogram;
+    }
+
+    public setDependencyRate(durationMs: number, successful: boolean): void {
+        if (successful) {
+            this._totalCount++;
+        }
+        else {
+            this._totalFailedCount++;
+        }
+        this._intervalExecutionTime += durationMs;
+    }
+
     private _getDependencyRate(observableResult: ObservableResult) {
         const last = this._lastDependencyRate;
         const currentTime = +new Date();
-        const intervalDependencys = this._httpMetrics.totalDependencyCount - last.count || 0;
+        const intervalDependencys = this._totalCount - last.count || 0;
         const elapsedMs = currentTime - last.time;
         if (elapsedMs > 0) {
             const elapsedSeconds = elapsedMs / 1000;
@@ -71,7 +90,7 @@ export class DependencyMetrics {
             observableResult.observe(DependencysPerSec);
         }
         this._lastDependencyRate = {
-            count: this._httpMetrics.totalDependencyCount,
+            count: this._totalCount,
             time: currentTime,
             executionInterval: last.executionInterval,
         };
@@ -80,7 +99,7 @@ export class DependencyMetrics {
     private _getFailureDependencyRate(observableResult: ObservableResult) {
         const last = this._lastFailureDependencyRate;
         const currentTime = +new Date();
-        const intervalDependencys = this._httpMetrics.totalFailedDependencyCount - last.count || 0;
+        const intervalDependencys = this._totalFailedCount - last.count || 0;
         const elapsedMs = currentTime - last.time;
         if (elapsedMs > 0) {
             const elapsedSeconds = elapsedMs / 1000;
@@ -88,7 +107,7 @@ export class DependencyMetrics {
             observableResult.observe(DependencysPerSec);
         }
         this._lastFailureDependencyRate = {
-            count: this._httpMetrics.totalFailedDependencyCount,
+            count: this._totalFailedCount,
             time: currentTime,
             executionInterval: last.executionInterval,
         };
diff --git a/src/metrics/collection/exceptionMetrics.ts b/src/metrics/collection/exceptionMetrics.ts
index 243cae8..d36240d 100644
--- a/src/metrics/collection/exceptionMetrics.ts
+++ b/src/metrics/collection/exceptionMetrics.ts
@@ -9,10 +9,10 @@ import {
 import {
     AggregatedMetricCounter,
     IStandardMetricBaseDimensions,
-    IMetricExceptionDimensions,
     MetricName,
 } from "../types";
 
+
 export class ExceptionMetrics {
     private _meter: Meter;
     private _exceptionCountersCollection: Array<AggregatedMetricCounter>;
@@ -36,7 +36,7 @@ export class ExceptionMetrics {
         ]);
     }
 
-    public countException(dimensions: IMetricExceptionDimensions) {
+    public countException(dimensions: IStandardMetricBaseDimensions) {
         const counter: AggregatedMetricCounter = this._getAggregatedCounter(
             dimensions,
             this._exceptionCountersCollection
diff --git a/src/metrics/collection/requestMetrics.ts b/src/metrics/collection/requestMetrics.ts
index c88f722..95771e5 100644
--- a/src/metrics/collection/requestMetrics.ts
+++ b/src/metrics/collection/requestMetrics.ts
@@ -1,28 +1,37 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 import {
+    Histogram,
     Meter,
     ObservableCallback,
     ObservableGauge,
     ObservableResult,
     ValueType,
 } from "@opentelemetry/api";
-import { AzureHttpMetricsInstrumentation } from "./azureHttpMetricsInstrumentation";
+import { ReadableSpan } from "@opentelemetry/sdk-trace-base";
+import { SemanticAttributes } from "@opentelemetry/semantic-conventions";
 import { MetricName } from "../types";
 
+
 export class RequestMetrics {
     private _meter: Meter;
-    private _httpMetrics: AzureHttpMetricsInstrumentation;
+    private _httpDurationHistogram: Histogram;
     private _requestRateGauge: ObservableGauge;
     private _requestRateGaugeCallback: ObservableCallback;
     private _requestFailureRateGauge: ObservableGauge;
     private _requestFailureRateGaugeCallback: ObservableCallback;
+    private _totalCount = 0;
+    private _totalFailedCount = 0;
+    private _intervalExecutionTime = 0;
     private _lastRequestRate: { count: number; time: number; executionInterval: number };
     private _lastFailureRequestRate: { count: number; time: number; executionInterval: number };
 
-    constructor(meter: Meter, httpMetrics: AzureHttpMetricsInstrumentation) {
+    constructor(meter: Meter) {
         this._meter = meter;
-        this._httpMetrics = httpMetrics;
+        this._httpDurationHistogram = this._meter.createHistogram(
+            MetricName.REQUEST_DURATION,
+            { valueType: ValueType.DOUBLE }
+        );
         this._lastRequestRate = { count: 0, time: 0, executionInterval: 0 };
         this._lastFailureRequestRate = { count: 0, time: 0, executionInterval: 0 };
         this._requestRateGauge = this._meter.createObservableGauge(MetricName.REQUEST_RATE, {
@@ -40,14 +49,14 @@ export class RequestMetrics {
     public enable(isEnabled: boolean) {
         if (isEnabled) {
             this._lastRequestRate = {
-                count: this._httpMetrics.totalRequestCount,
+                count: this._totalCount,
                 time: +new Date(),
-                executionInterval: this._httpMetrics.intervalRequestExecutionTime,
+                executionInterval: this._intervalExecutionTime,
             };
             this._lastFailureRequestRate = {
-                count: this._httpMetrics.totalFailedRequestCount,
+                count: this._totalFailedCount,
                 time: +new Date(),
-                executionInterval: this._httpMetrics.intervalRequestExecutionTime,
+                executionInterval: this._intervalExecutionTime,
             };
             this._requestRateGauge.addCallback(this._requestRateGaugeCallback);
             this._requestFailureRateGauge.addCallback(this._requestFailureRateGaugeCallback);
@@ -57,10 +66,31 @@ export class RequestMetrics {
         }
     }
 
+    public getDurationHistogram(): Histogram {
+        return this._httpDurationHistogram;
+    }
+
+    public setRequestRate(span: ReadableSpan): void {
+        const durationMs = span.duration[0];
+        let success = false;
+        const statusCode = parseInt(String(span.attributes[SemanticAttributes.HTTP_STATUS_CODE]));
+        if (!isNaN(statusCode)) {
+            success = 0 < statusCode && statusCode < 500;
+        }
+
+        if (success) {
+            this._totalCount++;
+        }
+        else {
+            this._totalFailedCount++;
+        }
+        this._intervalExecutionTime += durationMs;
+    }
+
     private _getRequestRate(observableResult: ObservableResult) {
         const currentTime = +new Date();
         const intervalRequests =
-            this._httpMetrics.totalRequestCount - this._lastRequestRate.count || 0;
+            this._totalCount - this._lastRequestRate.count || 0;
         const elapsedMs = currentTime - this._lastRequestRate.time;
         if (elapsedMs > 0) {
             const elapsedSeconds = elapsedMs / 1000;
@@ -68,7 +98,7 @@ export class RequestMetrics {
             observableResult.observe(requestsPerSec);
         }
         this._lastRequestRate = {
-            count: this._httpMetrics.totalRequestCount,
+            count: this._totalCount,
             time: currentTime,
             executionInterval: this._lastRequestRate.executionInterval,
         };
@@ -77,7 +107,7 @@ export class RequestMetrics {
     private _getFailureRequestRate(observableResult: ObservableResult) {
         const currentTime = +new Date();
         const intervalRequests =
-            this._httpMetrics.totalFailedDependencyCount - this._lastFailureRequestRate.count || 0;
+            this._totalFailedCount - this._lastFailureRequestRate.count || 0;
         const elapsedMs = currentTime - this._lastFailureRequestRate.time;
         if (elapsedMs > 0) {
             const elapsedSeconds = elapsedMs / 1000;
@@ -85,7 +115,7 @@ export class RequestMetrics {
             observableResult.observe(requestsPerSec);
         }
         this._lastFailureRequestRate = {
-            count: this._httpMetrics.totalFailedDependencyCount,
+            count: this._totalFailedCount,
             time: currentTime,
             executionInterval: this._lastFailureRequestRate.executionInterval,
         };
diff --git a/src/metrics/handlers/performanceCounterMetricsHandler.ts b/src/metrics/handlers/performanceCounterMetricsHandler.ts
index e93c75e..ae7d787 100644
--- a/src/metrics/handlers/performanceCounterMetricsHandler.ts
+++ b/src/metrics/handlers/performanceCounterMetricsHandler.ts
@@ -4,7 +4,7 @@ import {
     AzureMonitorExporterOptions,
     AzureMonitorMetricExporter,
 } from "@azure/monitor-opentelemetry-exporter";
-import { Meter } from "@opentelemetry/api";
+import { Meter, SpanKind } from "@opentelemetry/api";
 import {
     DropAggregation,
     MeterProvider,
@@ -18,12 +18,12 @@ import {
     NativeMetricsCounter,
     PerformanceCounter,
 } from "../types";
-import { AzureHttpMetricsInstrumentation } from "../collection/azureHttpMetricsInstrumentation";
 import { ProcessMetrics } from "../collection/processMetrics";
 import { RequestMetrics } from "../collection/requestMetrics";
 import { ApplicationInsightsConfig } from "../../shared";
 import { NativePerformanceMetrics } from "../collection/nativePerformanceMetrics";
-import { Util } from "../../shared/util";
+import { ReadableSpan } from "@opentelemetry/sdk-trace-base";
+
 
 export class PerformanceCounterMetricsHandler {
     private _config: ApplicationInsightsConfig;
@@ -32,7 +32,6 @@ export class PerformanceCounterMetricsHandler {
     private _azureExporter: AzureMonitorMetricExporter;
     private _metricReader: PeriodicExportingMetricReader;
     private _meter: Meter;
-    private _httpMetrics: AzureHttpMetricsInstrumentation;
     private _processMetrics: ProcessMetrics;
     private _requestMetrics: RequestMetrics;
     private _nativeMetrics: NativePerformanceMetrics;
@@ -58,11 +57,8 @@ export class PerformanceCounterMetricsHandler {
         this._metricReader = new PeriodicExportingMetricReader(metricReaderOptions);
         this._meterProvider.addMetricReader(this._metricReader);
         this._meter = this._meterProvider.getMeter("ApplicationInsightsPerfMetricsMeter");
-        this._httpMetrics = new AzureHttpMetricsInstrumentation({
-            ignoreOutgoingRequestHook: Util.getInstance().ignoreOutgoingRequestHook
-        });
         this._processMetrics = new ProcessMetrics(this._meter);
-        this._requestMetrics = new RequestMetrics(this._meter, this._httpMetrics);
+        this._requestMetrics = new RequestMetrics(this._meter);
         this._nativeMetrics = new NativePerformanceMetrics(this._meter);
     }
 
@@ -76,16 +72,10 @@ export class PerformanceCounterMetricsHandler {
         this._meterProvider.shutdown();
     }
 
-    public getHttpMetricsInstrumentation(): AzureHttpMetricsInstrumentation {
-        return this._httpMetrics;
-    }
-
-    public getProcessMetrics(): ProcessMetrics {
-        return this._processMetrics;
-    }
-
-    public getRequestMetrics(): RequestMetrics {
-        return this._requestMetrics;
+    public recordSpan(span: ReadableSpan): void {
+        if (span.kind === SpanKind.SERVER) {
+            this._requestMetrics.setRequestRate(span);
+        }
     }
 
     private _getViews(): View[] {
@@ -94,7 +84,6 @@ export class PerformanceCounterMetricsHandler {
             new View({
                 name: PerformanceCounter.REQUEST_DURATION,
                 instrumentName: MetricName.REQUEST_DURATION,
-                attributeKeys: [], // Drop all dimensions
             })
         );
         views.push(
diff --git a/src/metrics/handlers/standardMetricsHandler.ts b/src/metrics/handlers/standardMetricsHandler.ts
index f0cb26a..ee3d6c6 100644
--- a/src/metrics/handlers/standardMetricsHandler.ts
+++ b/src/metrics/handlers/standardMetricsHandler.ts
@@ -2,7 +2,7 @@ import {
     AzureMonitorExporterOptions,
     AzureMonitorMetricExporter,
 } from "@azure/monitor-opentelemetry-exporter";
-import { Meter } from "@opentelemetry/api";
+import { Attributes, Meter, SpanKind } from "@opentelemetry/api";
 import {
     DropAggregation,
     MeterProvider,
@@ -11,10 +11,14 @@ import {
     PeriodicExportingMetricReaderOptions,
     View,
 } from "@opentelemetry/sdk-metrics";
+import { ReadableSpan } from "@opentelemetry/sdk-trace-base";
+import { SemanticAttributes, SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
 import { ApplicationInsightsConfig } from "../../shared";
+import { DependencyMetrics } from "../collection/dependencyMetrics";
 import { ExceptionMetrics } from "../collection/exceptionMetrics";
+import { RequestMetrics } from "../collection/requestMetrics";
 import { TraceMetrics } from "../collection/traceMetrics";
-import { MetricName, StandardMetric } from "../types";
+import { IMetricDependencyDimensions, IMetricRequestDimensions, IMetricTraceDimensions, IStandardMetricBaseDimensions, MetricName, PreAggregatedMetricPropertyNames, StandardMetric, StandardMetricIds } from "../types";
 
 export class StandardMetricsHandler {
     private _config: ApplicationInsightsConfig;
@@ -23,6 +27,8 @@ export class StandardMetricsHandler {
     private _azureExporter: AzureMonitorMetricExporter;
     private _metricReader: PeriodicExportingMetricReader;
     private _meter: Meter;
+    private _dependencyMetrics: DependencyMetrics;
+    private _requestMetrics: RequestMetrics;
     private _exceptionMetrics: ExceptionMetrics;
     private _traceMetrics: TraceMetrics;
 
@@ -47,24 +53,101 @@ export class StandardMetricsHandler {
         this._metricReader = new PeriodicExportingMetricReader(metricReaderOptions);
         this._meterProvider.addMetricReader(this._metricReader);
         this._meter = this._meterProvider.getMeter("ApplicationInsightsStandardMetricsMeter");
+        this._requestMetrics = new RequestMetrics(this._meter);
+        this._dependencyMetrics = new DependencyMetrics(this._meter);
         this._exceptionMetrics = new ExceptionMetrics(this._meter);
         this._traceMetrics = new TraceMetrics(this._meter);
     }
 
+    public async flush(): Promise<void> {
+        await this._meterProvider.forceFlush();
+    }
+
     public shutdown() {
         this._meterProvider.shutdown();
     }
 
-    public getMeterProvider(): MeterProvider {
-        return this._meterProvider;
+    public recordException(dimensions: IMetricTraceDimensions): void {
+        dimensions.metricId = StandardMetricIds.EXCEPTION_COUNT;
+        this._exceptionMetrics.countException(dimensions);
+    }
+
+    public recordTrace(dimensions: IMetricTraceDimensions): void {
+        dimensions.metricId = StandardMetricIds.TRACE_COUNT;
+        this._traceMetrics.countTrace(dimensions);
+    }
+
+    public recordSpan(span: ReadableSpan): void {
+        const durationMs = span.duration[0];
+        if (span.kind === SpanKind.SERVER) {
+            this._requestMetrics.getDurationHistogram().record(durationMs, this._getStandardMetricRequestDimensions(span));
+        }
+        else {
+            this._dependencyMetrics.getDurationHistogram().record(durationMs, this._getStandardMetricDependencyDimensions(span));
+        }
+    }
+
+    private _getStandardMetricRequestDimensions(span: ReadableSpan): Attributes {
+        const dimensions: IMetricRequestDimensions = this._getStandardMetricBaseDimensions(span);
+        dimensions.metricId = StandardMetricIds.REQUEST_DURATION;
+        const statusCode = String(span.attributes["http.status_code"]);
+        dimensions.requestResultCode = statusCode;
+        dimensions.requestSuccess = statusCode === "200" ? "True" : "False";
+        return dimensions as Attributes;
+    }
+
+    private _getStandardMetricDependencyDimensions(span: ReadableSpan): Attributes {
+        const dimensions: IMetricDependencyDimensions = this._getStandardMetricBaseDimensions(span);
+        dimensions.metricId = StandardMetricIds.DEPENDENCY_DURATION;
+        const statusCode = String(span.attributes["http.status_code"]);
+        dimensions.dependencyTarget = this._getDependencyTarget(span.attributes);
+        dimensions.dependencyResultCode = statusCode;
+        dimensions.dependencyType = "http";
+        dimensions.dependencySuccess = statusCode === "200" ? "True" : "False";
+        return dimensions as Attributes;
     }
 
-    public getExceptionMetrics(): ExceptionMetrics {
-        return this._exceptionMetrics;
+    private _getStandardMetricBaseDimensions(span: ReadableSpan): IStandardMetricBaseDimensions {
+        const dimensions: IStandardMetricBaseDimensions = {};
+        dimensions.IsAutocollected = "True";
+        if (span.resource) {
+            const spanResourceAttributes = span.resource.attributes;
+            const serviceName = spanResourceAttributes[SemanticResourceAttributes.SERVICE_NAME];
+            const serviceNamespace = spanResourceAttributes[SemanticResourceAttributes.SERVICE_NAMESPACE];
+            if (serviceName) {
+                if (serviceNamespace) {
+                    dimensions.cloudRoleName = `${serviceNamespace}.${serviceName}`;
+                } else {
+                    dimensions.cloudRoleName = String(serviceName);
+                }
+            }
+            const serviceInstanceId = spanResourceAttributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID];
+            dimensions.cloudRoleInstance = String(serviceInstanceId);
+        }
+        return dimensions;
     }
 
-    public getTraceMetrics(): TraceMetrics {
-        return this._traceMetrics;
+    private _getDependencyTarget(attributes: Attributes): string {
+        if (!attributes) {
+            return "";
+        }
+        const peerService = attributes[SemanticAttributes.PEER_SERVICE];
+        const httpHost = attributes[SemanticAttributes.HTTP_HOST];
+        const httpUrl = attributes[SemanticAttributes.HTTP_URL];
+        const netPeerName = attributes[SemanticAttributes.NET_PEER_NAME];
+        const netPeerIp = attributes[SemanticAttributes.NET_PEER_IP];
+        if (peerService) {
+            return String(peerService);
+        } else if (httpHost) {
+            return String(httpHost);
+        } else if (httpUrl) {
+            return String(httpUrl);
+        } else if (netPeerName) {
+            return String(netPeerName);
+        } else if (netPeerIp) {
+            return String(netPeerIp);
+        }
+        return "";
     }
 
     private _getViews(): View[] {
@@ -72,13 +155,13 @@ export class StandardMetricsHandler {
         views.push(
             new View({
                 name: StandardMetric.HTTP_REQUEST_DURATION,
-                instrumentName: "http.server.duration", // Metric semantic conventions not available yet
+                instrumentName: MetricName.REQUEST_DURATION
             })
         );
         views.push(
             new View({
                 name: StandardMetric.HTTP_DEPENDENCY_DURATION,
-                instrumentName: "http.client.duration", // Metric semantic conventions not available yet
+                instrumentName: MetricName.DEPENDENCY_DURATION,
             })
         );
         views.push(
@@ -94,6 +177,30 @@ export class StandardMetricsHandler {
             })
         );
         // Ignore list
+        views.push(
+            new View({
+                instrumentName: MetricName.REQUEST_RATE,
+                aggregation: new DropAggregation(),
+            })
+        );
+        views.push(
+            new View({
+                instrumentName: MetricName.REQUEST_FAILURE_RATE,
+                aggregation: new DropAggregation(),
+            })
+        );
+        views.push(
+            new View({
+                instrumentName: MetricName.DEPENDENCY_RATE,
+                aggregation: new DropAggregation(),
+            })
+        );
+        views.push(
+            new View({
+                instrumentName: MetricName.DEPENDENCY_FAILURE_RATE,
+                aggregation: new DropAggregation(),
+            })
+        );
         views.push(
             new View({
                 instrumentName: MetricName.EXCEPTION_RATE,
@@ -108,8 +215,4 @@ export class StandardMetricsHandler {
         );
         return views;
     }
-
-    public async flush(): Promise<void> {
-        await this._meterProvider.forceFlush();
-    }
 }
diff --git a/src/metrics/metricHandler.ts b/src/metrics/metricHandler.ts
index 7e42bb8..3ae8a67 100644
--- a/src/metrics/metricHandler.ts
+++ b/src/metrics/metricHandler.ts
@@ -1,5 +1,8 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
+import { SpanKind } from "@opentelemetry/api";
+import { ReadableSpan, Span, TimedEvent } from "@opentelemetry/sdk-trace-base";
+import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
 import { ApplicationInsightsConfig } from "../shared";
 import {
     CustomMetricsHandler,
@@ -7,8 +10,7 @@ import {
     StandardMetricsHandler,
     PerformanceCounterMetricsHandler,
 } from "./handlers";
-import { AzureHttpMetricsInstrumentation } from "./collection/azureHttpMetricsInstrumentation";
-import { IMetricExceptionDimensions, IMetricTraceDimensions } from "./types";
+import { IMetricTraceDimensions, IStandardMetricBaseDimensions } from "./types";
 
 export class MetricHandler {
     private _config: ApplicationInsightsConfig;
@@ -43,6 +45,13 @@ export class MetricHandler {
         this._heartbeatHandler?.shutdown();
     }
 
+    public async flush(): Promise<void> {
+        await this._customMetricsHandler.flush();
+        await this._heartbeatHandler?.flush();
+        await this._standardMetricsHandler?.flush();
+        await this._perfCounterMetricsHandler?.flush();
+    }
+
     public getConfig(): ApplicationInsightsConfig {
         return this._config;
     }
@@ -51,26 +60,57 @@ export class MetricHandler {
         return this._customMetricsHandler;
     }
 
-    public getStandardMetricsHandler(): StandardMetricsHandler {
-        return this._standardMetricsHandler;
+    public markSpanAsProcceseded(span: Span): void {
+        if (this._config.enableAutoCollectStandardMetrics) {
+            if (span.kind === SpanKind.CLIENT) {
+                span.setAttributes({
+                    "_MS.ProcessedByMetricExtractors": "(Name:'Dependencies', Ver:'1.1')",
+                });
+            } else if (span.kind === SpanKind.SERVER) {
+                span.setAttributes({
+                    "_MS.ProcessedByMetricExtractors": "(Name:'Requests', Ver:'1.1')",
+                });
+            }
+        }
     }
 
-    public getPerCounterAzureHttpInstrumentation(): AzureHttpMetricsInstrumentation {
-        return this._perfCounterMetricsHandler?.getHttpMetricsInstrumentation();
+    public recordException(dimensions: IStandardMetricBaseDimensions): void {
+        this._standardMetricsHandler?.recordException(dimensions);
     }
 
-    public countException(dimensions: IMetricExceptionDimensions): void {
-        this._standardMetricsHandler?.getExceptionMetrics().countException(dimensions);
+    public recordTrace(dimensions: IMetricTraceDimensions): void {
+        this._standardMetricsHandler?.recordTrace(dimensions);
     }
 
-    public countTrace(dimensions: IMetricTraceDimensions): void {
-        this._standardMetricsHandler?.getTraceMetrics().countTrace(dimensions);
+    public recordSpan(span: ReadableSpan): void {
+        this._standardMetricsHandler?.recordSpan(span);
+        this._perfCounterMetricsHandler?.recordSpan(span);
     }
 
-    public async flush(): Promise<void> {
-        await this._customMetricsHandler.flush();
-        await this._heartbeatHandler?.flush();
-        await this._standardMetricsHandler?.flush();
-        await this._perfCounterMetricsHandler?.flush();
+    public recordSpanEvents(span: ReadableSpan): void {
+        if (span.events) {
+            span.events.forEach((event: TimedEvent) => {
+                const dimensions: IStandardMetricBaseDimensions = {
+                    cloudRoleInstance: "",
+                    cloudRoleName: "",
+                };
+                const serviceName =
+                    span.resource?.attributes[SemanticResourceAttributes.SERVICE_NAME];
+                const serviceNamespace =
+                    span.resource?.attributes[SemanticResourceAttributes.SERVICE_NAMESPACE];
+                if (serviceName) {
+                    if (serviceNamespace) {
+                        dimensions.cloudRoleInstance = `${serviceNamespace}.${serviceName}`;
+                    } else {
+                        dimensions.cloudRoleName = String(serviceName);
+                    }
+                }
+                if (event.name === "exception") {
+                    this._standardMetricsHandler?.recordException(dimensions);
+                } else {
+                    this._standardMetricsHandler?.recordTrace(dimensions);
+                }
+            });
+        }
     }
 }
diff --git a/src/metrics/types.ts b/src/metrics/types.ts
index 57e4667..0322799 100644
--- a/src/metrics/types.ts
+++ b/src/metrics/types.ts
@@ -1,10 +1,6 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
-
-import { IncomingMessage, RequestOptions } from "http";
-
 import { Attributes, SpanKind } from "@opentelemetry/api";
-import { InstrumentationConfig } from "@opentelemetry/instrumentation";
 
 export enum MetricName {
     // Memory
@@ -81,14 +77,6 @@ export enum GarbageCollectionType {
     IncrementalMarking = "IncrementalMarking",
 }
 
-export class AggregatedMetric {
-    public name: string;
-    public dimensions: { [key: string]: any };
-    public value: number;
-    public count: number;
-    public aggregationInterval: number;
-}
-
 export class AggregatedMetricCounter {
     public time: number;
     public lastTime: number;
@@ -108,28 +96,35 @@ export class AggregatedMetricCounter {
     }
 }
 
+export enum StandardMetricIds {
+    REQUEST_DURATION = "requests/duration",
+    DEPENDENCY_DURATION = "dependencies/duration",
+    EXCEPTION_COUNT = "exceptions/count",
+    TRACE_COUNT = "traces/count",
+  }
+
 export interface IStandardMetricBaseDimensions {
+    metricId?: string;
     cloudRoleInstance?: string;
     cloudRoleName?: string;
+    IsAutocollected?: string;
 }
 
-export type IMetricExceptionDimensions = IStandardMetricBaseDimensions;
-
 export interface IMetricTraceDimensions extends IStandardMetricBaseDimensions {
     traceSeverityLevel?: string;
 }
 
-export type MetricDimensionTypeKeys =
-    | "cloudRoleInstance"
-    | "cloudRoleName"
-    | "requestSuccess"
-    | "requestResultCode"
-    | "dependencyType"
-    | "dependencyTarget"
-    | "dependencySuccess"
-    | "dependencyResultCode"
-    | "traceSeverityLevel"
-    | "operationSynthetic";
+export interface IMetricRequestDimensions extends IStandardMetricBaseDimensions {
+    requestSuccess?: string;
+    requestResultCode?: string;
+}
+
+export interface IMetricDependencyDimensions extends IStandardMetricBaseDimensions {
+    dependencyType?: string;
+    dependencyTarget?: string;
+    dependencySuccess?: string;
+    dependencyResultCode?: string;
+}
 
 export interface IHttpStandardMetric {
     startTime: number;
@@ -138,6 +133,20 @@ export interface IHttpStandardMetric {
     attributes: Attributes;
 }
 
+export type MetricDimensionTypeKeys =
+  | "cloudRoleInstance"
+  | "cloudRoleName"
+  | "requestSuccess"
+  | "requestResultCode"
+  | "dependencyType"
+  | "dependencyTarget"
+  | "dependencySuccess"
+  | "dependencyResultCode"
+  | "traceSeverityLevel"
+  | "operationSynthetic"
+  | "metricId"
+  | "IsAutocollected";
+
 // Names expected in Breeze side for dimensions
 export const PreAggregatedMetricPropertyNames: { [key in MetricDimensionTypeKeys]: string } = {
     cloudRoleInstance: "cloud/roleInstance",
@@ -150,4 +159,6 @@ export const PreAggregatedMetricPropertyNames: { [key in MetricDimensionTypeKeys
     dependencySuccess: "Dependency.Success",
     dependencyResultCode: "dependency/resultCode",
     traceSeverityLevel: "trace/severityLevel",
+    metricId: "_MS.MetricId",
+    IsAutocollected: "_MS.IsAutocollected",
 };
diff --git a/src/traces/applicationInsightsSampler.ts b/src/traces/applicationInsightsSampler.ts
new file mode 100644
index 0000000..7cb5078
--- /dev/null
+++ b/src/traces/applicationInsightsSampler.ts
@@ -0,0 +1,94 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+import { Link, Attributes, SpanKind, Context } from "@opentelemetry/api";
+import { Sampler, SamplingDecision, SamplingResult } from "@opentelemetry/sdk-trace-base";
+
+
+const AzureMonitorSampleRate = "_MS.sampleRate";
+
+/**
+ * ApplicationInsightsSampler is responsible for the following:
+ * Implements same trace id hashing algorithm so that traces are sampled the same across multiple nodes
+ * Adds item count to span attribute if span is sampled (needed for ingestion service)
+ * @param samplingRatio - 0 to 1 value.
+ */
+export class ApplicationInsightsSampler implements Sampler {
+    private readonly _sampleRate: number;
+    private readonly _samplingRatio: number;
+
+    constructor(samplingRatio = 1) {
+        this._samplingRatio = samplingRatio;
+        if (this._samplingRatio > 1) {
+            throw new Error("Wrong sampling rate, data will not be sampled out");
+        }
+        this._sampleRate = Math.round(this._samplingRatio * 100);
+    }
+
+    /**
+     * Checks whether span needs to be created and tracked.
+     *
+     * @param context Parent Context which may contain a span.
+     * @param traceId of the span to be created. It can be different from the
+     *     traceId in the {@link SpanContext}. Typically in situations when the
+     *     span to be created starts a new trace.
+     * @param spanName of the span to be created.
+     * @param spanKind of the span to be created.
+     * @param attributes Initial set of SpanAttributes for the Span being constructed.
+     * @param links Collection of links that will be associated with the Span to
+     *     be created. Typically useful for batch operations.
+     * @returns a {@link SamplingResult}.
+     */
+    public shouldSample(
+        context: Context,
+        traceId: string,
+        spanName: string,
+        spanKind: SpanKind,
+        attributes: Attributes,
+        links: Link[]
+    ): SamplingResult {
+        let isSampledIn = false;
+        if (this._sampleRate === 100) {
+            isSampledIn = true;
+        } else if (this._sampleRate === 0) {
+            isSampledIn = false;
+        } else {
+            isSampledIn = this._getSamplingHashCode(traceId) < this._sampleRate;
+        }
+        // Add sample rate as span attribute
+        attributes = attributes || {};
+        attributes[AzureMonitorSampleRate] = this._sampleRate;
+        // When not sampled in record anyways so we can calculate pre aggregated metrics, perf counters and live metrics.
+        return isSampledIn
+            ? { decision: SamplingDecision.RECORD_AND_SAMPLED, attributes: attributes }
+            : { decision: SamplingDecision.RECORD, attributes: attributes };
+    }
+
+    /**
+     * Return Sampler description
+     */
+    public toString(): string {
+        return `ApplicationInsightsSampler{${this._samplingRatio}}`;
+    }
+
+    private _getSamplingHashCode(input: string): number {
+        const csharpMin = -2147483648;
+        const csharpMax = 2147483647;
+        let hash = 5381;
+
+        if (!input) {
+            return 0;
+        }
+
+        while (input.length < 8) {
+            input = input + input;
+        }
+
+        for (let i = 0; i < input.length; i++) {
+            // JS doesn't respond to integer overflow by wrapping around. Simulate it with bitwise operators ( | 0)
+            hash = ((((hash << 5) + hash) | 0) + input.charCodeAt(i)) | 0;
+        }
+
+        hash = hash <= csharpMin ? csharpMax : Math.abs(hash);
+        return (hash / csharpMax) * 100;
+    }
+}
diff --git a/src/traces/azureSpanProcessor.ts b/src/traces/azureSpanProcessor.ts
index bfbbbac..9aa9d0e 100644
--- a/src/traces/azureSpanProcessor.ts
+++ b/src/traces/azureSpanProcessor.ts
@@ -1,60 +1,26 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
-
-import { Context, SpanKind } from "@opentelemetry/api";
-import { ReadableSpan, Span, SpanProcessor, TimedEvent } from "@opentelemetry/sdk-trace-base";
-import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
-import { IStandardMetricBaseDimensions } from "../metrics/types";
+import { Context } from "@opentelemetry/api";
+import { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base";
 import { MetricHandler } from "../metrics/metricHandler";
 
+
 export class AzureSpanProcessor implements SpanProcessor {
-    constructor(private readonly _metricHandler: MetricHandler) {}
+    constructor(private readonly _metricHandler: MetricHandler) { }
 
     forceFlush(): Promise<void> {
         return Promise.resolve();
     }
 
     onStart(span: Span, context: Context): void {
-        if (this._metricHandler.getConfig().enableAutoCollectStandardMetrics) {
-            if (span.instrumentationLibrary.name === "@opentelemetry/instrumentation-http") {
-                if (span.kind === SpanKind.CLIENT) {
-                    span.setAttributes({
-                        "_MS.ProcessedByMetricExtractors": "(Name:'Dependencies', Ver:'1.1')",
-                    });
-                } else if (span.kind === SpanKind.SERVER) {
-                    span.setAttributes({
-                        "_MS.ProcessedByMetricExtractors": "(Name:'Requests', Ver:'1.1')",
-                    });
-                }
-            }
-        }
+        this._metricHandler.markSpanAsProcceseded(span);
     }
 
     onEnd(span: ReadableSpan): void {
-        if (span.events) {
-            span.events.forEach((event: TimedEvent) => {
-                const dimensions: IStandardMetricBaseDimensions = {
-                    cloudRoleInstance: "",
-                    cloudRoleName: "",
-                };
-                const serviceName =
-                    span.resource?.attributes[SemanticResourceAttributes.SERVICE_NAME];
-                const serviceNamespace =
-                    span.resource?.attributes[SemanticResourceAttributes.SERVICE_NAMESPACE];
-                if (serviceName) {
-                    if (serviceNamespace) {
-                        dimensions.cloudRoleInstance = `${serviceNamespace}.${serviceName}`;
-                    } else {
-                        dimensions.cloudRoleName = String(serviceName);
-                    }
-                }
-                if (event.name === "exception") {
-                    this._metricHandler.countException(dimensions);
-                } else {
-                    this._metricHandler.countTrace(dimensions);
-                }
-            });
-        }
+        // Record duration metrics
+        this._metricHandler.recordSpan(span);
+        // Calculate exception and log metrics
+        this._metricHandler.recordSpanEvents(span);
     }
 
     shutdown(): Promise<void> {
diff --git a/src/traces/traceHandler.ts b/src/traces/traceHandler.ts
index bf32b0f..2e584fa 100644
--- a/src/traces/traceHandler.ts
+++ b/src/traces/traceHandler.ts
@@ -2,7 +2,6 @@
 // Licensed under the MIT license.
 import { RequestOptions } from "http";
 import {
-    ApplicationInsightsSampler,
     AzureMonitorExporterOptions,
     AzureMonitorTraceExporter,
 } from "@azure/monitor-opentelemetry-exporter";
@@ -16,15 +15,15 @@ import { RedisInstrumentation as Redis4Instrumentation } from "@opentelemetry/in
 import { NodeTracerProvider, NodeTracerConfig } from "@opentelemetry/sdk-trace-node";
 import { BatchSpanProcessor, BufferConfig, Tracer } from "@opentelemetry/sdk-trace-base";
 import { HttpInstrumentation, HttpInstrumentationConfig, IgnoreOutgoingRequestFunction } from "@opentelemetry/instrumentation-http";
-
+import { ApplicationInsightsSampler } from "./applicationInsightsSampler";
 import { ApplicationInsightsConfig } from "../shared";
 import { TracerProvider } from "@opentelemetry/api";
 import { MetricHandler } from "../metrics/metricHandler";
 import { AzureSpanProcessor } from "./azureSpanProcessor";
-import { AzureHttpMetricsInstrumentation } from "../metrics/collection/azureHttpMetricsInstrumentation";
 import { AzureFunctionsHook } from "./azureFunctionsHook";
 import { Util } from "../shared/util";
 
+
 export class TraceHandler {
     private _exporter: AzureMonitorTraceExporter;
     private _spanProcessor: BatchSpanProcessor;
@@ -34,7 +33,6 @@ export class TraceHandler {
     private _tracerProvider: NodeTracerProvider;
     private _tracer: Tracer;
     private _azureFunctionsHook: AzureFunctionsHook;
-    private _perfCountersHttpInstrumentation: AzureHttpMetricsInstrumentation;
     private _httpInstrumentation: Instrumentation;
     private _azureSdkInstrumentation: Instrumentation;
     private _mongoDbInstrumentation: Instrumentation;
@@ -91,21 +89,11 @@ export class TraceHandler {
     }
 
     public start() {
-        if (this._metricHandler) {
-            if (!this._perfCountersHttpInstrumentation) {
-                this._perfCountersHttpInstrumentation =
-                    this._metricHandler.getPerCounterAzureHttpInstrumentation();
-                if (this._perfCountersHttpInstrumentation) {
-                    // Null when configured off
-                    this.addInstrumentation(this._perfCountersHttpInstrumentation);
-                }
-            }
-        }
         if (!this._httpInstrumentation) {
-            let httpInstrumentationConfig = (this._config.instrumentations.http as HttpInstrumentationConfig);
-            let providedIgnoreOutgoingRequestHook = httpInstrumentationConfig.ignoreOutgoingRequestHook;
-            let mergedIgnoreOutgoingRequestHook: IgnoreOutgoingRequestFunction = (request: RequestOptions) => {
-                let result = Util.getInstance().ignoreOutgoingRequestHook(request);
+            const httpInstrumentationConfig = (this._config.instrumentations.http as HttpInstrumentationConfig);
+            const providedIgnoreOutgoingRequestHook = httpInstrumentationConfig.ignoreOutgoingRequestHook;
+            const mergedIgnoreOutgoingRequestHook: IgnoreOutgoingRequestFunction = (request: RequestOptions) => {
+                const result = Util.getInstance().ignoreOutgoingRequestHook(request);
                 if (!result) { // Not internal call
                     if (providedIgnoreOutgoingRequestHook) { // Provided hook in config
                         return providedIgnoreOutgoingRequestHook(request);
@@ -115,11 +103,6 @@ export class TraceHandler {
             };
             httpInstrumentationConfig.ignoreOutgoingRequestHook = mergedIgnoreOutgoingRequestHook;
             this._httpInstrumentation = new HttpInstrumentation(this._config.instrumentations.http);
-            if (this._metricHandler && this._metricHandler.getStandardMetricsHandler()) {
-                this._httpInstrumentation.setMeterProvider(
-                    this._metricHandler.getStandardMetricsHandler().getMeterProvider()
-                );
-            }
             this.addInstrumentation(this._httpInstrumentation);
         }
         if (!this._azureSdkInstrumentation) {


Test patch:
diff --git a/test/unitTests/metrics/azureHttpMetricsInstrumentation.tests.ts b/test/unitTests/metrics/azureHttpMetricsInstrumentation.tests.ts
deleted file mode 100644
index 61b7b64..0000000
--- a/test/unitTests/metrics/azureHttpMetricsInstrumentation.tests.ts
+++ /dev/null
@@ -1,175 +0,0 @@
-import * as assert from "assert";
-import * as sinon from "sinon";
-import * as nock from "nock";
-import { AzureMonitorMetricExporter } from "@azure/monitor-opentelemetry-exporter";
-import {
-    DataPointType,
-    MeterProvider,
-    PeriodicExportingMetricReader,
-} from "@opentelemetry/sdk-metrics";
-import { SemanticAttributes } from "@opentelemetry/semantic-conventions";
-import { AzureHttpMetricsInstrumentation } from "../../../src/metrics/collection/azureHttpMetricsInstrumentation";
-import { Util } from "../../../src/shared/util";
-
-nock("https://centralus-0.in.applicationinsights.azure.com")
-    .post("/v2.1/track", (body: string) => true)
-    .reply(200, {})
-    .persist();
-
-const instrumentation = new AzureHttpMetricsInstrumentation({
-    ignoreOutgoingRequestHook: Util.getInstance().ignoreOutgoingRequestHook
-});
-instrumentation.enable();
-instrumentation.disable();
-
-import * as http from "http";
-
-const meterProvider = new MeterProvider();
-const exporter = new AzureMonitorMetricExporter({
-    connectionString:
-        "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/",
-});
-const metricReader = new PeriodicExportingMetricReader({
-    exporter: exporter as any,
-    exportIntervalMillis: 100,
-});
-meterProvider.addMetricReader(metricReader);
-instrumentation.setMeterProvider(meterProvider);
-
-describe("AutoCollection/AzureHttpMetricsInstrumentation", () => {
-    let sandbox: sinon.SinonSandbox;
-    let mockHttpServer: any;
-    let mockHttpServerPort = 0;
-
-    before(() => {
-        instrumentation.enable();
-        sandbox = sinon.createSandbox();
-        createMockServer();
-    });
-
-    after(() => {
-        instrumentation.disable();
-        meterProvider.shutdown();
-        mockHttpServer.close();
-        nock.cleanAll();
-    });
-
-    function createMockServer() {
-        mockHttpServer = http.createServer((req: any, res: any) => {
-            res.statusCode = 200;
-            res.setHeader("content-type", "application/json");
-            res.write(
-                JSON.stringify({
-                    success: true,
-                })
-            );
-            res.end();
-        });
-        mockHttpServer.listen(0, () => {
-            const addr = mockHttpServer.address();
-            if (addr == null) {
-                new Error("unexpected addr null");
-                return;
-            }
-            if (typeof addr === "string") {
-                new Error(`unexpected addr ${addr}`);
-                return;
-            }
-            if (addr.port <= 0) {
-                new Error("Could not get port");
-                return;
-            }
-            mockHttpServerPort = addr.port;
-        });
-    }
-
-    async function makeHttpRequest(): Promise<void> {
-        const options = {
-            hostname: "localhost",
-            port: mockHttpServerPort,
-            path: "/test",
-            method: "GET",
-        };
-        return new Promise((resolve, reject) => {
-            const req = http.request(options, (res: any) => {
-                res.on("data", function () { });
-                res.on("end", () => {
-                    resolve();
-                });
-            });
-            req.on("error", (error: Error) => {
-                reject(error);
-            });
-            req.end();
-        });
-    }
-
-    it("Server/Client Metric Duration", async () => {
-        const mockExport = sandbox.stub(exporter, "export");
-        await makeHttpRequest();
-        await new Promise((resolve) => setTimeout(resolve, 250));
-        assert.ok(mockExport.called);
-        const resourceMetrics = mockExport.args[0][0];
-        const scopeMetrics = resourceMetrics.scopeMetrics;
-        assert.strictEqual(scopeMetrics.length, 1, "scopeMetrics count");
-        const metrics = scopeMetrics[0].metrics;
-        assert.strictEqual(metrics.length, 2, "metrics count");
-        assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM);
-        assert.strictEqual(metrics[0].descriptor.name, "REQUEST_DURATION");
-        assert.strictEqual(metrics[0].dataPoints.length, 1);
-        assert.strictEqual((metrics[0].dataPoints[0].value as any).count, 1);
-        assert.strictEqual(
-            metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_SCHEME],
-            "http"
-        );
-        assert.strictEqual(
-            metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_METHOD],
-            "GET"
-        );
-        assert.strictEqual(
-            metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_FLAVOR],
-            "1.1"
-        );
-        assert.strictEqual(
-            metrics[0].dataPoints[0].attributes[SemanticAttributes.NET_HOST_NAME],
-            "localhost"
-        );
-        assert.strictEqual(
-            metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_TARGET],
-            "/test"
-        );
-        assert.strictEqual(
-            metrics[0].dataPoints[0].attributes[SemanticAttributes.HTTP_STATUS_CODE],
-            "200"
-        );
-        assert.strictEqual(
-            metrics[0].dataPoints[0].attributes[SemanticAttributes.NET_HOST_PORT],
-            mockHttpServerPort.toString()
-        );
-
-        assert.strictEqual(metrics[1].dataPointType, DataPointType.HISTOGRAM);
-        assert.strictEqual(metrics[1].descriptor.name, "DEPENDENCY_DURATION");
-        assert.strictEqual(metrics[1].dataPoints.length, 1);
-        assert.strictEqual((metrics[1].dataPoints[0].value as any).count, 1);
-        assert.strictEqual(
-            metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_METHOD],
-            "GET"
-        );
-        assert.strictEqual(
-            metrics[1].dataPoints[0].attributes[SemanticAttributes.NET_PEER_NAME],
-            "localhost"
-        );
-        assert.strictEqual(
-            metrics[1].dataPoints[0].attributes[SemanticAttributes.NET_PEER_PORT],
-            mockHttpServerPort.toString()
-        );
-        assert.strictEqual(
-            metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_STATUS_CODE],
-            "200"
-        );
-        assert.strictEqual(
-            metrics[1].dataPoints[0].attributes[SemanticAttributes.HTTP_FLAVOR],
-            "1.1"
-        );
-    });
-});
diff --git a/test/unitTests/metrics/metricHandler.tests.ts b/test/unitTests/metrics/metricHandler.tests.ts
index fe33c9b..ddae54a 100644
--- a/test/unitTests/metrics/metricHandler.tests.ts
+++ b/test/unitTests/metrics/metricHandler.tests.ts
@@ -32,7 +32,7 @@ describe("Library/MetricHandler", () => {
             const handler = new MetricHandler(_config);
             handler["_perfCounterMetricsHandler"]["_nativeMetrics"]["_metricsAvailable"] = false;
             handler.start();
-            assert.ok(handler.getStandardMetricsHandler());
+            assert.ok(handler["_standardMetricsHandler"]);
         });
 
         it("heartbeat metrics enablement during start", () => {
diff --git a/test/unitTests/metrics/performance.tests.ts b/test/unitTests/metrics/performance.tests.ts
index 126acac..9fc9eec 100644
--- a/test/unitTests/metrics/performance.tests.ts
+++ b/test/unitTests/metrics/performance.tests.ts
@@ -28,27 +28,23 @@ describe("PerformanceCounterMetricsHandler", () => {
     describe("#Metrics", () => {
         it("should create instruments", () => {
             assert.ok(
-                autoCollect.getHttpMetricsInstrumentation()["_httpServerDurationHistogram"],
-                "_httpServerDurationHistogram not available"
-            );
-            assert.ok(
-                autoCollect.getProcessMetrics()["_memoryPrivateBytesGauge"],
+                autoCollect["_processMetrics"]["_memoryPrivateBytesGauge"],
                 "_memoryPrivateBytesGauge not available"
             );
             assert.ok(
-                autoCollect.getProcessMetrics()["_memoryAvailableBytesGauge"],
+                autoCollect["_processMetrics"]["_memoryAvailableBytesGauge"],
                 "_memoryAvailableBytesGauge not available"
             );
             assert.ok(
-                autoCollect.getProcessMetrics()["_processorTimeGauge"],
+                autoCollect["_processMetrics"]["_processorTimeGauge"],
                 "_processorTimeGauge not available"
             );
             assert.ok(
-                autoCollect.getProcessMetrics()["_processTimeGauge"],
+                autoCollect["_processMetrics"]["_processTimeGauge"],
                 "_processTimeGauge not available"
             );
             assert.ok(
-                autoCollect.getRequestMetrics()["_requestRateGauge"],
+                autoCollect["_requestMetrics"]["_requestRateGauge"],
                 "_dependencyDurationGauge not available"
             );
 
@@ -85,47 +81,35 @@ describe("PerformanceCounterMetricsHandler", () => {
         it("should observe instruments during collection", async () => {
             const mockExport = sandbox.stub(autoCollect["_azureExporter"], "export");
             autoCollect.start();
-            autoCollect
-                .getHttpMetricsInstrumentation()
-                .setMeterProvider(autoCollect["_meterProvider"]);
-            autoCollect.getHttpMetricsInstrumentation()["_httpRequestDone"]({
-                startTime: Date.now() - 100,
-                isProcessed: false,
-                spanKind: SpanKind.SERVER,
-                attributes: { HTTP_STATUS_CODE: "200" },
-            });
-
             await new Promise((resolve) => setTimeout(resolve, 120));
             assert.ok(mockExport.called);
             const resourceMetrics = mockExport.args[0][0];
             const scopeMetrics = resourceMetrics.scopeMetrics;
-            assert.strictEqual(scopeMetrics.length, 2, "scopeMetrics count");
+            assert.strictEqual(scopeMetrics.length, 1, "scopeMetrics count");
             let metrics = scopeMetrics[0].metrics;
-            assert.strictEqual(metrics.length, 12, "metrics count");
+            assert.strictEqual(metrics.length, 13, "metrics count");
             assert.equal(metrics[0].descriptor.name, PerformanceCounter.PRIVATE_BYTES);
             assert.equal(metrics[1].descriptor.name, PerformanceCounter.AVAILABLE_BYTES);
             assert.equal(metrics[2].descriptor.name, PerformanceCounter.PROCESSOR_TIME);
             assert.equal(metrics[3].descriptor.name, PerformanceCounter.PROCESS_TIME);
-            assert.equal(metrics[4].descriptor.name, PerformanceCounter.REQUEST_RATE);
-            assert.equal(metrics[5].descriptor.name, NativeMetricsCounter.EVENT_LOOP_CPU);
+            assert.equal(metrics[4].descriptor.name, PerformanceCounter.REQUEST_DURATION);
+            assert.equal(metrics[5].descriptor.name, PerformanceCounter.REQUEST_RATE);
+            assert.equal(metrics[6].descriptor.name, NativeMetricsCounter.EVENT_LOOP_CPU);
             assert.equal(
-                metrics[6].descriptor.name,
+                metrics[7].descriptor.name,
                 NativeMetricsCounter.GARBAGE_COLLECTION_SCAVENGE
             );
             assert.equal(
-                metrics[7].descriptor.name,
+                metrics[8].descriptor.name,
                 NativeMetricsCounter.GARBAGE_COLLECTION_SWEEP_COMPACT
             );
             assert.equal(
-                metrics[8].descriptor.name,
+                metrics[9].descriptor.name,
                 NativeMetricsCounter.GARBAGE_COLLECTION_INCREMENTAL_MARKING
             );
-            assert.equal(metrics[9].descriptor.name, NativeMetricsCounter.HEAP_MEMORY_TOTAL);
-            assert.equal(metrics[10].descriptor.name, NativeMetricsCounter.HEAP_MEMORY_USAGE);
-            assert.equal(metrics[11].descriptor.name, NativeMetricsCounter.MEMORY_USAGE_NON_HEAP);
-            metrics = scopeMetrics[1].metrics;
-            assert.strictEqual(metrics.length, 1, "metrics count");
-            assert.equal(metrics[0].descriptor.name, PerformanceCounter.REQUEST_DURATION);
+            assert.equal(metrics[10].descriptor.name, NativeMetricsCounter.HEAP_MEMORY_TOTAL);
+            assert.equal(metrics[11].descriptor.name, NativeMetricsCounter.HEAP_MEMORY_USAGE);
+            assert.equal(metrics[12].descriptor.name, NativeMetricsCounter.MEMORY_USAGE_NON_HEAP);
         });
 
         it("should not collect when disabled", async () => {
diff --git a/test/unitTests/metrics/standardMetrics.tests.ts b/test/unitTests/metrics/standardMetrics.tests.ts
index 78e704e..a4df7a8 100644
--- a/test/unitTests/metrics/standardMetrics.tests.ts
+++ b/test/unitTests/metrics/standardMetrics.tests.ts
@@ -1,12 +1,11 @@
-import {
-    HttpInstrumentation,
-    HttpInstrumentationConfig,
-} from "@opentelemetry/instrumentation-http";
+import { SpanKind } from "@opentelemetry/api";
+import { Histogram } from "@opentelemetry/sdk-metrics";
+import { SemanticAttributes, SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
 import * as assert from "assert";
 import * as sinon from "sinon";
 
 import { StandardMetricsHandler } from "../../../src/metrics/handlers/standardMetricsHandler";
-import { IMetricExceptionDimensions, StandardMetric } from "../../../src/metrics/types";
+import { IStandardMetricBaseDimensions, StandardMetric } from "../../../src/metrics/types";
 import { ApplicationInsightsConfig } from "../../../src/shared";
 
 describe("#StandardMetricsHandler", () => {
@@ -31,31 +30,71 @@ describe("#StandardMetricsHandler", () => {
 
     it("should create instruments", () => {
         assert.ok(
-            autoCollect.getExceptionMetrics()["_exceptionsRateGauge"],
+            autoCollect["_exceptionMetrics"]["_exceptionsRateGauge"],
             "_exceptionsRateGauge not available"
         );
         assert.ok(
-            autoCollect.getTraceMetrics()["_tracesRateGauge"],
+            autoCollect["_traceMetrics"]["_tracesRateGauge"],
             "_tracesRateGauge not available"
         );
     });
 
     it("should observe instruments during collection", async () => {
         const mockExport = sandbox.stub(autoCollect["_azureExporter"], "export");
-        // autoCollect.start();
-        let dimensions: IMetricExceptionDimensions = {
+        let resource = {
+            attributes: {} as any
+        };
+
+        let dimensions: IStandardMetricBaseDimensions = {
             cloudRoleInstance: "testcloudRoleInstance",
             cloudRoleName: "testcloudRoleName",
         };
-        autoCollect.getExceptionMetrics().countException(dimensions);
-        autoCollect.getTraceMetrics().countTrace(dimensions);
+        autoCollect.recordException(dimensions);
+        autoCollect.recordTrace(dimensions);
+
+        resource.attributes[SemanticResourceAttributes.SERVICE_NAME] = dimensions.cloudRoleName;
+        resource.attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID] = dimensions.cloudRoleInstance;
+
+        let clientSpan: any = {
+            kind: SpanKind.CLIENT,
+            duration: [123456],
+            attributes: {
+                "http.status_code": 200,
+            },
+            resource: resource
+        };
+        clientSpan.attributes[SemanticAttributes.PEER_SERVICE] = "testPeerService";
+        autoCollect.recordSpan(clientSpan);
+        clientSpan.attributes["http.status_code"] = "400";
+        autoCollect.recordSpan(clientSpan);
+
+        let serverSpan: any = {
+            kind: SpanKind.SERVER,
+            duration: [654321],
+            attributes: {
+                "http.status_code": 200
+            },
+            resource: resource
+        };
+        autoCollect.recordSpan(serverSpan);
+        serverSpan.attributes["http.status_code"] = "400";
+        autoCollect.recordSpan(serverSpan);
+
+
         dimensions = {
             cloudRoleInstance: "testcloudRoleInstance2",
             cloudRoleName: "testcloudRoleName2",
         };
+        resource.attributes[SemanticResourceAttributes.SERVICE_NAME] = dimensions.cloudRoleName;
+        resource.attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID] = dimensions.cloudRoleInstance;
+
         for (let i = 0; i < 10; i++) {
-            autoCollect.getExceptionMetrics().countException(dimensions);
-            autoCollect.getTraceMetrics().countTrace(dimensions);
+            autoCollect.recordException(dimensions);
+            autoCollect.recordTrace(dimensions);
+            clientSpan.duration[0] = i * 100000;
+            autoCollect.recordSpan(clientSpan);
+            serverSpan.duration[0] = i * 100000;
+            autoCollect.recordSpan(serverSpan);
         }
 
         await new Promise((resolve) => setTimeout(resolve, 120));
@@ -64,46 +103,117 @@ describe("#StandardMetricsHandler", () => {
         const scopeMetrics = resourceMetrics.scopeMetrics;
         assert.strictEqual(scopeMetrics.length, 1, "scopeMetrics count");
         const metrics = scopeMetrics[0].metrics;
-        assert.strictEqual(metrics.length, 2, "metrics count");
-        assert.equal(metrics[0].descriptor.name, StandardMetric.EXCEPTION_COUNT);
-        assert.equal(metrics[1].descriptor.name, StandardMetric.TRACE_COUNT);
-        assert.strictEqual(metrics[0].dataPoints.length, 2, "dataPoints count");
+        assert.strictEqual(metrics.length, 4, "metrics count");
+        assert.equal(metrics[0].descriptor.name, StandardMetric.HTTP_REQUEST_DURATION);
+        assert.equal(metrics[1].descriptor.name, StandardMetric.HTTP_DEPENDENCY_DURATION);
+        assert.equal(metrics[2].descriptor.name, StandardMetric.EXCEPTION_COUNT);
+        assert.equal(metrics[3].descriptor.name, StandardMetric.TRACE_COUNT);
+
+        // Requests
+        
+        assert.strictEqual(metrics[0].dataPoints.length, 3, "dataPoints count");
+        assert.strictEqual((metrics[0].dataPoints[0].value as Histogram).count, 1, "dataPoint count");
+        assert.strictEqual((metrics[0].dataPoints[0].value as Histogram).min, 654321, "dataPoint min");
+        assert.strictEqual((metrics[0].dataPoints[0].value as Histogram).max, 654321, "dataPoint max");
+        assert.strictEqual((metrics[0].dataPoints[0].value as Histogram).sum, 654321, "dataPoint sum");
+        assert.strictEqual(metrics[0].dataPoints[0].attributes["cloudRoleInstance"], "testcloudRoleInstance");
+        assert.strictEqual(metrics[0].dataPoints[0].attributes["cloudRoleName"], "testcloudRoleName");
+        assert.strictEqual(metrics[0].dataPoints[0].attributes["IsAutocollected"], "True");
+        assert.strictEqual(metrics[0].dataPoints[0].attributes["metricId"], "requests/duration");
+        assert.strictEqual(metrics[0].dataPoints[0].attributes["requestResultCode"], "200");
+        assert.strictEqual(metrics[0].dataPoints[0].attributes["requestSuccess"], "True");
+
+        assert.strictEqual((metrics[0].dataPoints[1].value as Histogram).count, 1, "dataPoint count");
+        assert.strictEqual((metrics[0].dataPoints[1].value as Histogram).min, 654321, "dataPoint min");
+        assert.strictEqual((metrics[0].dataPoints[1].value as Histogram).max, 654321, "dataPoint max");
+        assert.strictEqual((metrics[0].dataPoints[1].value as Histogram).sum, 654321, "dataPoint sum");
+        assert.strictEqual(metrics[0].dataPoints[1].attributes["cloudRoleInstance"], "testcloudRoleInstance");
+        assert.strictEqual(metrics[0].dataPoints[1].attributes["cloudRoleName"], "testcloudRoleName");
+        assert.strictEqual(metrics[0].dataPoints[1].attributes["IsAutocollected"], "True");
+        assert.strictEqual(metrics[0].dataPoints[1].attributes["metricId"], "requests/duration");
+        assert.strictEqual(metrics[0].dataPoints[1].attributes["requestResultCode"], "400");
+        assert.strictEqual(metrics[0].dataPoints[1].attributes["requestSuccess"], "False");
+
+        assert.strictEqual((metrics[0].dataPoints[2].value as Histogram).count, 10, "dataPoint count");
+        assert.strictEqual((metrics[0].dataPoints[2].value as Histogram).min, 0, "dataPoint min");
+        assert.strictEqual((metrics[0].dataPoints[2].value as Histogram).max, 900000, "dataPoint max");
+        assert.strictEqual((metrics[0].dataPoints[2].value as Histogram).sum, 4500000, "dataPoint sum");
+        assert.strictEqual(metrics[0].dataPoints[2].attributes["cloudRoleInstance"], "testcloudRoleInstance2");
+        assert.strictEqual(metrics[0].dataPoints[2].attributes["cloudRoleName"], "testcloudRoleName2");
+        assert.strictEqual(metrics[0].dataPoints[2].attributes["IsAutocollected"], "True");
+        assert.strictEqual(metrics[0].dataPoints[2].attributes["metricId"], "requests/duration");
+        assert.strictEqual(metrics[0].dataPoints[2].attributes["requestResultCode"], "400");
+        assert.strictEqual(metrics[0].dataPoints[2].attributes["requestSuccess"], "False");
+
+        // Dependencies
+        assert.strictEqual(metrics[1].dataPoints.length, 3, "dataPoints count");
+        assert.strictEqual((metrics[1].dataPoints[0].value as Histogram).count, 1, "dataPoint count");
+        assert.strictEqual((metrics[1].dataPoints[0].value as Histogram).min, 123456, "dataPoint min");
+        assert.strictEqual((metrics[1].dataPoints[0].value as Histogram).max, 123456, "dataPoint max");
+        assert.strictEqual((metrics[1].dataPoints[0].value as Histogram).sum, 123456, "dataPoint sum");
+        assert.strictEqual(metrics[1].dataPoints[0].attributes["metricId"], "dependencies/duration");
+        assert.strictEqual(metrics[1].dataPoints[0].attributes["dependencyTarget"], "testPeerService");
+        assert.strictEqual(metrics[1].dataPoints[0].attributes["dependencyResultCode"], "200");
+        assert.strictEqual(metrics[1].dataPoints[0].attributes["dependencyType"], "http");
+        assert.strictEqual(metrics[1].dataPoints[0].attributes["dependencySuccess"], "True");
+
+        assert.strictEqual((metrics[1].dataPoints[1].value as Histogram).count, 1, "dataPoint count");
+        assert.strictEqual((metrics[1].dataPoints[1].value as Histogram).min, 123456, "dataPoint min");
+        assert.strictEqual((metrics[1].dataPoints[1].value as Histogram).max, 123456, "dataPoint max");
+        assert.strictEqual((metrics[1].dataPoints[1].value as Histogram).sum, 123456, "dataPoint sum");
+        assert.strictEqual(metrics[1].dataPoints[1].attributes["metricId"], "dependencies/duration");
+        assert.strictEqual(metrics[1].dataPoints[1].attributes["dependencyTarget"], "testPeerService");
+        assert.strictEqual(metrics[1].dataPoints[1].attributes["dependencyResultCode"], "400");
+        assert.strictEqual(metrics[1].dataPoints[1].attributes["dependencyType"], "http");
+        assert.strictEqual(metrics[1].dataPoints[1].attributes["dependencySuccess"], "False");
+
+        assert.strictEqual((metrics[1].dataPoints[2].value as Histogram).count, 10, "dataPoint count");
+        assert.strictEqual((metrics[1].dataPoints[2].value as Histogram).min, 0, "dataPoint min");
+        assert.strictEqual((metrics[1].dataPoints[2].value as Histogram).max, 900000, "dataPoint max");
+        assert.strictEqual((metrics[1].dataPoints[2].value as Histogram).sum, 4500000, "dataPoint sum");
+        assert.strictEqual(metrics[1].dataPoints[2].attributes["metricId"], "dependencies/duration");
+        assert.strictEqual(metrics[1].dataPoints[2].attributes["dependencyTarget"], "testPeerService");
+        assert.strictEqual(metrics[1].dataPoints[2].attributes["dependencyResultCode"], "400");
+        assert.strictEqual(metrics[1].dataPoints[2].attributes["dependencyType"], "http");
+        assert.strictEqual(metrics[1].dataPoints[2].attributes["dependencySuccess"], "False");
+
         // Exceptions
-        assert.strictEqual(metrics[0].dataPoints[0].value, 1, "dataPoint value");
+        assert.strictEqual(metrics[2].dataPoints.length, 2, "dataPoints count");
+        assert.strictEqual(metrics[2].dataPoints[0].value, 1, "dataPoint value");
         assert.strictEqual(
-            metrics[0].dataPoints[0].attributes["cloudRoleInstance"],
+            metrics[2].dataPoints[0].attributes["cloudRoleInstance"],
             "testcloudRoleInstance"
         );
         assert.strictEqual(
-            metrics[0].dataPoints[0].attributes["cloudRoleName"],
+            metrics[2].dataPoints[0].attributes["cloudRoleName"],
             "testcloudRoleName"
         );
-        assert.strictEqual(metrics[0].dataPoints[1].value, 10, "dataPoint value");
+        assert.strictEqual(metrics[2].dataPoints[1].value, 10, "dataPoint value");
         assert.strictEqual(
-            metrics[0].dataPoints[1].attributes["cloudRoleInstance"],
+            metrics[2].dataPoints[1].attributes["cloudRoleInstance"],
             "testcloudRoleInstance2"
         );
         assert.strictEqual(
-            metrics[0].dataPoints[1].attributes["cloudRoleName"],
+            metrics[2].dataPoints[1].attributes["cloudRoleName"],
             "testcloudRoleName2"
         );
         // Traces
-        assert.strictEqual(metrics[1].dataPoints[0].value, 1, "dataPoint value");
+        assert.strictEqual(metrics[3].dataPoints[0].value, 1, "dataPoint value");
         assert.strictEqual(
-            metrics[1].dataPoints[0].attributes["cloudRoleInstance"],
+            metrics[3].dataPoints[0].attributes["cloudRoleInstance"],
             "testcloudRoleInstance"
         );
         assert.strictEqual(
-            metrics[1].dataPoints[0].attributes["cloudRoleName"],
+            metrics[3].dataPoints[0].attributes["cloudRoleName"],
             "testcloudRoleName"
         );
-        assert.strictEqual(metrics[1].dataPoints[1].value, 10, "dataPoint value");
+        assert.strictEqual(metrics[3].dataPoints[1].value, 10, "dataPoint value");
         assert.strictEqual(
-            metrics[1].dataPoints[1].attributes["cloudRoleInstance"],
+            metrics[3].dataPoints[1].attributes["cloudRoleInstance"],
             "testcloudRoleInstance2"
         );
         assert.strictEqual(
-            metrics[1].dataPoints[1].attributes["cloudRoleName"],
+            metrics[3].dataPoints[1].attributes["cloudRoleName"],
             "testcloudRoleName2"
         );
     });
@@ -114,98 +224,4 @@ describe("#StandardMetricsHandler", () => {
         await new Promise((resolve) => setTimeout(resolve, 120));
         assert.ok(mockExport.notCalled);
     });
-
-    describe("#HTTP incoming/outgoing requests duration", () => {
-        let http: any = null;
-        let mockHttpServer: any;
-        let mockHttpServerPort = 0;
-
-        before(() => {
-            const config = new ApplicationInsightsConfig();
-            config.connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;";
-            autoCollect = new StandardMetricsHandler(config, { collectionInterval: 100 });
-
-            const httpConfig: HttpInstrumentationConfig = {
-                enabled: true,
-            };
-            const instrumentation = new HttpInstrumentation(httpConfig);
-            instrumentation.setMeterProvider(autoCollect.getMeterProvider());
-            instrumentation.enable();
-            // Load Http modules, HTTP instrumentation hook will be created in OpenTelemetry
-            http = require("http") as any;
-            createMockServers();
-        });
-
-        after(() => {
-            mockHttpServer.close();
-        });
-
-        function createMockServers() {
-            mockHttpServer = http.createServer((req: any, res: any) => {
-                res.statusCode = 200;
-                res.setHeader("content-type", "application/json");
-                res.write(
-                    JSON.stringify({
-                        success: true,
-                    })
-                );
-                res.end();
-            });
-            mockHttpServer.listen(0, () => {
-                const addr = mockHttpServer.address();
-                if (addr == null) {
-                    new Error("unexpected addr null");
-                    return;
-                }
-                if (typeof addr === "string") {
-                    new Error(`unexpected addr ${addr}`);
-                    return;
-                }
-                if (addr.port <= 0) {
-                    new Error("Could not get port");
-                    return;
-                }
-                mockHttpServerPort = addr.port;
-            });
-        }
-
-        async function makeHttpRequest(): Promise<void> {
-            const options = {
-                hostname: "localhost",
-                port: mockHttpServerPort,
-                path: "/test",
-                method: "GET",
-            };
-            return new Promise((resolve, reject) => {
-                const req = http.request(options, (res: any) => {
-                    res.on("data", function () {});
-                    res.on("end", () => {
-                        resolve();
-                    });
-                });
-                req.on("error", (error: Error) => {
-                    reject(error);
-                });
-                req.end();
-            });
-        }
-
-        it("http outgoing/incoming requests", async () => {
-            const mockExport = sandbox.stub(autoCollect["_azureExporter"], "export");
-            await makeHttpRequest();
-            await new Promise((resolve) => setTimeout(resolve, 120));
-            assert.ok(mockExport.called);
-            const resourceMetrics = mockExport.args[0][0];
-            const scopeMetrics = resourceMetrics.scopeMetrics;
-            assert.strictEqual(scopeMetrics.length, 2, "scopeMetrics count");
-            let metrics = scopeMetrics[0].metrics;
-            assert.strictEqual(metrics.length, 2, "metrics count");
-            assert.equal(metrics[0].descriptor.name, StandardMetric.EXCEPTION_COUNT);
-            assert.equal(metrics[1].descriptor.name, StandardMetric.TRACE_COUNT);
-            metrics = scopeMetrics[1].metrics;
-            assert.strictEqual(metrics.length, 2, "metrics count");
-            assert.equal(metrics[0].descriptor.name, StandardMetric.HTTP_REQUEST_DURATION);
-            assert.equal(metrics[1].descriptor.name, StandardMetric.HTTP_DEPENDENCY_DURATION);
-        });
-    });
 });
diff --git a/test/unitTests/traces/traceHandler.tests.ts b/test/unitTests/traces/traceHandler.tests.ts
index ce8705a..574512a 100644
--- a/test/unitTests/traces/traceHandler.tests.ts
+++ b/test/unitTests/traces/traceHandler.tests.ts
@@ -26,22 +26,6 @@ describe("Library/TraceHandler", () => {
         sandbox.restore();
     });
 
-    describe("#Instrumentation Enablement", () => {
-        it("AzureHttpMetricsInstrumentation", () => {
-            _config.enableAutoCollectPerformance = true;
-            const metricHandler = new MetricHandler(_config);
-            const handler = new TraceHandler(_config, metricHandler);
-            handler.start();
-            let found = false;
-            handler["_instrumentations"].forEach((instrumentation: Instrumentation) => {
-                if (instrumentation.instrumentationName == "AzureHttpMetricsInstrumentation") {
-                    found = true;
-                }
-            });
-            assert.ok(found, "AzureHttpMetricsInstrumentation not added");
-        });
-    });
-
     describe("#autoCollection of HTTP/HTTPS requests", () => {
         let exportStub: sinon.SinonStub;
         let handler: TraceHandler = null;