Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update TelemetryClient to enable tracking of dialog ('page') views #3440

Merged
merged 14 commits into from
Mar 4, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;

namespace Microsoft.Bot.Builder.ApplicationInsights
{
/// <summary>
/// A logging client for bot telemetry.
/// </summary>
public class AppInsightsTelemetryClient : LogTelemetryClient
{
private readonly TelemetryClient _telemetryClient;

/// <summary>
/// Initializes a new instance of the <see cref="AppInsightsTelemetryClient"/> class.
/// </summary>
/// <param name="telemetryClient">The telemetry client to forward bot events to.</param>
public AppInsightsTelemetryClient(TelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient));
}

/// <summary>
/// Send information about availability of an application.
/// </summary>
/// <param name="name">Availability test name.</param>
/// <param name="timeStamp">The time when the availability was captured.</param>
/// <param name="duration">The time taken for the availability test to run.</param>
/// <param name="runLocation">Name of the location the availability test was run from.</param>
/// <param name="success">True if the availability test ran successfully.</param>
/// <param name="message">Error message on availability test run failure.</param>
/// <param name="properties">Named string values you can use to classify and search for this availability telemetry.</param>
/// <param name="metrics">Additional values associated with this availability telemetry.</param>
public virtual void TrackAvailability(string name, DateTimeOffset timeStamp, TimeSpan duration, string runLocation, bool success, string message = null, IDictionary<string, string> properties = null, IDictionary<string, double> metrics = null)
{
var telemetry = new AvailabilityTelemetry(name, timeStamp, duration, runLocation, success, message);
if (properties != null)
{
foreach (var pair in properties)
{
telemetry.Properties.Add(pair.Key, pair.Value);
}
}

if (metrics != null)
{
foreach (var pair in metrics)
{
telemetry.Metrics.Add(pair.Key, pair.Value);
}
}

_telemetryClient.TrackAvailability(telemetry);
}

/// <summary>
/// Send information about an external dependency (outgoing call) in the application.
/// </summary>
/// <param name="dependencyTypeName">Name of the command initiated with this dependency call. Low cardinality value.
/// Examples are SQL, Azure table, and HTTP.</param>
/// <param name="target">External dependency target.</param>
/// <param name="dependencyName">Name of the command initiated with this dependency call. Low cardinality value.
/// Examples are stored procedure name and URL path template.</param>
/// <param name="data">Command initiated by this dependency call. Examples are SQL statement and HTTP
/// URL's with all query parameters.</param>
/// <param name="startTime">The time when the dependency was called.</param>
/// <param name="duration">The time taken by the external dependency to handle the call.</param>
/// <param name="resultCode">Result code of dependency call execution.</param>
/// <param name="success">True if the dependency call was handled successfully.</param>
public virtual void TrackDependency(string dependencyTypeName, string target, string dependencyName, string data, DateTimeOffset startTime, TimeSpan duration, string resultCode, bool success)
{
var telemetry = new DependencyTelemetry
{
Type = dependencyTypeName,
Target = target,
Name = dependencyName,
Data = data,
Timestamp = startTime,
Duration = duration,
ResultCode = resultCode,
Success = success,
};

_telemetryClient.TrackDependency(telemetry);
}

/// <summary>
/// Logs custom events with extensible named fields.
/// </summary>
/// <param name="eventName">A name for the event.</param>
/// <param name="properties">Named string values you can use to search and classify events.</param>
/// <param name="metrics">Measurements associated with this event.</param>
public virtual void TrackEvent(string eventName, IDictionary<string, string> properties = null, IDictionary<string, double> metrics = null)
{
var telemetry = new EventTelemetry(eventName);
if (properties != null)
{
foreach (var pair in properties)
{
telemetry.Properties.Add(pair.Key, pair.Value);
}
}

if (metrics != null)
{
foreach (var pair in metrics)
{
telemetry.Metrics.Add(pair.Key, pair.Value);
}
}

_telemetryClient.TrackEvent(telemetry);
}

/// <summary>
/// Logs a system exception.
/// </summary>
/// <param name="exception">The exception to log.</param>
/// <param name="properties">Named string values you can use to classify and search for this exception.</param>
/// <param name="metrics">Additional values associated with this exception.</param>
public virtual void TrackException(Exception exception, IDictionary<string, string> properties = null, IDictionary<string, double> metrics = null)
{
var telemetry = new ExceptionTelemetry(exception);
if (properties != null)
{
foreach (var pair in properties)
{
telemetry.Properties.Add(pair.Key, pair.Value);
}
}

if (metrics != null)
{
foreach (var pair in metrics)
{
telemetry.Metrics.Add(pair.Key, pair.Value);
}
}

_telemetryClient.TrackException(telemetry);
}

/// <summary>
/// Send a trace message.
/// </summary>
/// <param name="message">Message to display.</param>
/// <param name="severityLevel">Trace severity level <see cref="Severity"/>.</param>
/// <param name="properties">Named string values you can use to search and classify events.</param>
public virtual void TrackTrace(string message, Severity severityLevel, IDictionary<string, string> properties)
{
var telemetry = new TraceTelemetry(message)
{
SeverityLevel = (SeverityLevel)severityLevel,
};

if (properties != null)
{
foreach (var pair in properties)
{
telemetry.Properties.Add(pair.Key, pair.Value);
}
}

_telemetryClient.TrackTrace(telemetry);
}

/// <summary>
/// Logs a an Application Insights page view.
/// </summary>
/// <param name="name">The name of the page view to log.</param>
/// <param name="properties">Named string values you can use to search and classify events.</param>
/// <param name="metrics">Measurements associated with this event.</param>
public virtual void TrackPageView(string name, IDictionary<string, string> properties = null, IDictionary<string, double> metrics = null)
{
var telemetry = new PageViewTelemetry(name);

if (properties != null)
{
foreach (var pair in properties)
{
telemetry.Properties.Add(pair.Key, pair.Value);
}
}

if (metrics != null)
{
foreach (var pair in metrics)
{
telemetry.Metrics.Add(pair.Key, pair.Value);
}
}

_telemetryClient.TrackPageView(telemetry);
}

/// <summary>
/// Flushes the in-memory buffer and any metrics being pre-aggregated.
/// </summary>
public virtual void Flush() => _telemetryClient.Flush();
}
}
24 changes: 24 additions & 0 deletions libraries/Microsoft.Bot.Builder.Dialogs/ComponentDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public ComponentDialog(string dialogId = null)
/// </summary>
/// <value>The <see cref="IBotTelemetryClient"/> to use when logging.</value>
/// <seealso cref="DialogSet.TelemetryClient"/>
[Obsolete("TelemetryClient is now obselete. Please use LogTelemetryClient instead.", false)]
public override IBotTelemetryClient TelemetryClient
{
get
Expand All @@ -52,6 +53,27 @@ public override IBotTelemetryClient TelemetryClient
}
}

/// <summary>
/// Gets or sets the <see cref="LogTelemetryClient"/> to use for logging.
/// When setting this property, all of the contained dialogs' <see cref="Dialog.TelemetryClient"/>
/// properties are also set.
/// </summary>
/// <value>The <see cref="LogTelemetryClient"/> to use when logging.</value>
/// <seealso cref="DialogSet.LogTelemetryClient"/>
public override LogTelemetryClient LogTelemetryClient
{
get
{
return base.LogTelemetryClient;
}

set
{
base.LogTelemetryClient = value ?? new NullLogTelemetryClient();
Dialogs.LogTelemetryClient = base.LogTelemetryClient;
}
}

/// <summary>
/// Called when the dialog is started and pushed onto the parent's dialog stack.
/// </summary>
Expand Down Expand Up @@ -88,6 +110,8 @@ public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext oute
return await EndComponentAsync(outerDc, turnResult.Result, cancellationToken).ConfigureAwait(false);
}

LogTelemetryClient.TrackPageView(Id);

// Just signal waiting
return Dialog.EndOfTurn;
}
Expand Down
34 changes: 34 additions & 0 deletions libraries/Microsoft.Bot.Builder.Dialogs/Dialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public abstract class Dialog
public static readonly DialogTurnResult EndOfTurn = new DialogTurnResult(DialogTurnStatus.Waiting);

private IBotTelemetryClient _telemetryClient;
private LogTelemetryClient _logTelemetryClient;

[JsonProperty("id")]
private string id;
Expand All @@ -37,6 +38,7 @@ public Dialog(string dialogId = null)
{
Id = dialogId;
_telemetryClient = NullBotTelemetryClient.Instance;
_logTelemetryClient = new NullLogTelemetryClient();
}

/// <summary>
Expand Down Expand Up @@ -66,6 +68,7 @@ public string Id
/// <value>The <see cref="IBotTelemetryClient"/> to use for logging.</value>
/// <seealso cref="DialogSet.TelemetryClient"/>
[JsonIgnore]
[Obsolete("TelemetryClient is now obselete. Please use LogTelemetryClient instead.", false)]
public virtual IBotTelemetryClient TelemetryClient
{
get
Expand All @@ -79,6 +82,25 @@ public virtual IBotTelemetryClient TelemetryClient
}
}

/// <summary>
/// Gets or sets the <see cref="LogTelemetryClient"/> to use for logging.
/// </summary>
/// <value>The <see cref="LogTelemetryClient"/> to use for logging.</value>
/// <seealso cref="DialogSet.LogTelemetryClient"/>
[JsonIgnore]
public virtual LogTelemetryClient LogTelemetryClient
{
get
{
return _logTelemetryClient;
}

set
{
_logTelemetryClient = value;
}
}

[JsonIgnore]
public virtual SourceRange Source => DebugSupport.SourceMap.TryGetValue(this, out var range) ? range : null;

Expand Down Expand Up @@ -243,6 +265,18 @@ protected virtual string OnComputeId()
return this.GetType().Name;
}

protected void TrackTelemetryEvent(string eventName, IDictionary<string, string> properties = null, IDictionary<string, double> metrics = null)
{
if (_logTelemetryClient != null && !(_logTelemetryClient is NullLogTelemetryClient))
{
_logTelemetryClient.TrackEvent(eventName, properties, metrics);
}
else
{
_telemetryClient.TrackEvent(eventName, properties, metrics);
}
}

protected void RegisterSourceLocation(string path, int lineNumber)
{
if (!string.IsNullOrEmpty(path))
Expand Down
30 changes: 30 additions & 0 deletions libraries/Microsoft.Bot.Builder.Dialogs/DialogSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class DialogSet
private readonly IDictionary<string, Dialog> _dialogs = new Dictionary<string, Dialog>();

private IBotTelemetryClient _telemetryClient;
private LogTelemetryClient _logTelemetryClient;

/// <summary>
/// Initializes a new instance of the <see cref="DialogSet"/> class.
Expand All @@ -32,12 +33,14 @@ public DialogSet(IStatePropertyAccessor<DialogState> dialogState)
{
_dialogState = dialogState ?? throw new ArgumentNullException(nameof(dialogState));
_telemetryClient = NullBotTelemetryClient.Instance;
_logTelemetryClient = new NullLogTelemetryClient();
}

public DialogSet()
{
_dialogState = null;
_telemetryClient = NullBotTelemetryClient.Instance;
_logTelemetryClient = new NullLogTelemetryClient();
}

/// <summary>
Expand All @@ -47,6 +50,7 @@ public DialogSet()
/// <remarks>When this property is set, it sets the <see cref="Dialog.TelemetryClient"/> of each
/// dialog in the set to the new value.</remarks>
[JsonIgnore]
[Obsolete("TelemetryClient is now obselete. Please use LogTelemetryClient instead.", false)]
public IBotTelemetryClient TelemetryClient
{
get
Expand All @@ -64,6 +68,30 @@ public IBotTelemetryClient TelemetryClient
}
}

/// <summary>
/// Gets or sets the <see cref="LogTelemetryClient"/> to use for logging.
/// </summary>
/// <value>The <see cref="LogTelemetryClient"/> to use for logging.</value>
/// <remarks>When this property is set, it sets the <see cref="Dialog.LogTelemetryClient"/> of each
/// dialog in the set to the new value.</remarks>
[JsonIgnore]
public LogTelemetryClient LogTelemetryClient
{
get
{
return _logTelemetryClient;
}

set
{
_logTelemetryClient = value ?? new NullLogTelemetryClient();
foreach (var dialog in _dialogs.Values)
{
dialog.LogTelemetryClient = _logTelemetryClient;
}
}
}

/// <summary>
/// Adds a new dialog to the set and returns the set to allow fluent chaining.
/// If the Dialog.Id being added already exists in the set, the dialogs id will be updated to
Expand Down Expand Up @@ -103,6 +131,8 @@ public DialogSet Add(Dialog dialog)
}

dialog.TelemetryClient = _telemetryClient;
dialog.LogTelemetryClient = _logTelemetryClient;

_dialogs[dialog.Id] = dialog;

// Automatically add any dependencies the dialog might have
Expand Down