Java tutorial
// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See License.txt in the project root. package com.microsoft.alm.plugin.telemetry; import com.microsoft.alm.plugin.context.ServerContext; import com.microsoft.alm.plugin.context.ServerContextManager; import com.microsoft.alm.plugin.services.PluginServiceProvider; import com.microsoft.applicationinsights.TelemetryClient; import com.microsoft.applicationinsights.TelemetryConfiguration; import com.microsoft.applicationinsights.channel.TelemetryChannel; import com.microsoft.applicationinsights.extensibility.ContextInitializer; import com.microsoft.applicationinsights.internal.logger.InternalLogger; import com.microsoft.applicationinsights.internal.logger.InternalLogger.LoggerOutputType; import com.microsoft.applicationinsights.telemetry.PageViewTelemetry; import com.microsoft.applicationinsights.telemetry.SessionState; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * The TfsTelemetryHelper class is a singleton that allows the plugin to capture * telemetry data when the user initiates events. */ public class TfsTelemetryHelper { // Static members private static final Logger logger = LoggerFactory.getLogger(TfsTelemetryHelper.class); public static final String UNIQUE_PREFIX = "ai-log"; //$NON-NLS-1$ public static final String BASE_FOLDER = "AppInsights"; //$NON-NLS-1$ private static final String UNKNOWN = "unknown"; //$NON-NLS-1$ // Instance members private TelemetryClient telemetryClient; // A private static class to allow safe lazy initialization of the singleton private static class TfsTelemetryHelperHolder { private static final TfsTelemetryHelper INSTANCE = new TfsTelemetryHelper(); } /** * The getInstance method returns the singleton instance. Creating it if necessary */ public static TfsTelemetryHelper getInstance() { // Using the Initialization-on-demand holder pattern to make sure this is thread-safe return TfsTelemetryHelperHolder.INSTANCE; } // The private constructor keeps the class from being inherited or misused private TfsTelemetryHelper() { final String skip = System.getProperties() .getProperty("com.microsoft.alm.plugin.telemetry.skipClientInitialization"); if (StringUtils.isNotEmpty(skip) && StringUtils.equalsIgnoreCase(skip, "true")) { // this flag is here for testing purposes in which case we do not want to create a telemetry channel // or client. return; } // Initialize the internal logger final Map<String, String> loggerData = new HashMap<String, String>(); loggerData.put("Level", InternalLogger.LoggingLevel.ERROR.toString()); //$NON-NLS-1$ loggerData.put("UniquePrefix", UNIQUE_PREFIX); //$NON-NLS-1$ loggerData.put("BaseFolder", BASE_FOLDER); //$NON-NLS-1$ InternalLogger.INSTANCE.initialize(LoggerOutputType.FILE.toString(), loggerData); // Initialize the active TelemetryConfiguration ContextInitializer initializer = PluginServiceProvider.getInstance().getTelemetryContextInitializer(); TelemetryConfiguration.getActive().getContextInitializers().add(initializer); // Create a channel to AppInsights final TelemetryChannel channel = TelemetryConfiguration.getActive().getChannel(); if (channel != null) { channel.setDeveloperMode(TfsTelemetryInstrumentationInfo.getInstance().isDeveloperMode()); } else { logger.error("Failed to load telemetry channel"); return; } // Create the telemetry client and cache it for later use logger.debug("AppInsights telemetry initialized"); //$NON-NLS-1$ logger.debug(MessageFormat.format(" Developer Mode: {0}", //$NON-NLS-1$ TfsTelemetryInstrumentationInfo.getInstance().isDeveloperMode())); logger.debug(MessageFormat.format(" Production Environment: {0}", //$NON-NLS-1$ !TfsTelemetryInstrumentationInfo.getInstance().isTestKey())); telemetryClient = new TelemetryClient(); telemetryClient.getContext().getSession().setId(UUID.randomUUID().toString()); } /** * Call sendMetric to track the new value of the named metric. * * @param name is the name of the metric to be tracked. * @param value is the current value of the metric as a double. */ public void sendMetric(final String name, final double value) { // Log that the event occurred (this log is used in testing) logger.debug(String.format("sendMetric(%s, %f)", name, value)); if (telemetryClient != null) { telemetryClient.trackMetric(name, value); } } /** * Call sendDialogOpened when a dialog is opened that you want to track telemetry for * * @param name is the name of the dialog to be tracked. * @param properties are additional properties to track with the event. */ public void sendDialogOpened(final String name, final Map<String, String> properties) { final PropertyMapBuilder builder = new PropertyMapBuilder(properties); final String pageName = String.format(TfsTelemetryConstants.DIALOG_PAGE_VIEW_NAME_FORMAT, name); // Log that the event occurred (this log is used in testing) logger.debug(String.format("sendDialogOpened(%s, %s)", pageName, builder.toString())); if (telemetryClient != null) { // Create the page view telemetry object to pass into the track page view method final PageViewTelemetry telemetry = new PageViewTelemetry(pageName); telemetry.getProperties().putAll(builder.build()); telemetryClient.trackPageView(telemetry); } } /** * Call sendEvent to track an occurrence of a named event. * * @param name is the name of the event to be tracked. * @param properties are additional properties to track with the event. */ public void sendEvent(final String name, final Map<String, String> properties) { final String eventName = String.format(TfsTelemetryConstants.PLUGIN_ACTION_EVENT_NAME_FORMAT, name); final PropertyMapBuilder builder = new PropertyMapBuilder(properties); // Log that the event occurred (this log is used in testing) logger.debug(String.format("sendEvent(%s, %s)", name, builder.toString())); if (telemetryClient != null) { telemetryClient.trackEvent(eventName, builder.build(), null); } } /** * Call sendSessionBegins when the plugin is loaded. */ public void sendSessionBegins() { sendSessionState(SessionState.Start); } /** * Call sendSessionEnds when the plugin is unloaded. */ public void sendSessionEnds() { sendSessionState(SessionState.End); } /** * Call sendException to track an exception that occurred that should be tracked. * * @param exception is the exception to track. */ public void sendException(final Exception exception, final Map<String, String> properties) { final PropertyMapBuilder builder = new PropertyMapBuilder(properties); // Log that the event occurred (this log is used in testing) logger.debug(String.format("sendException(%s, %s)", exception.getMessage(), builder.toString())); if (telemetryClient != null) { telemetryClient.trackException(exception, builder.build(), null); } } private void sendSessionState(final SessionState state) { // Log that the event occurred (this log is used in testing) logger.debug(String.format("sendSessionState(%s)", state.toString())); if (telemetryClient != null) { telemetryClient.trackSessionState(state); } } public static class PropertyMapBuilder { public static final Map<String, String> EMPTY = new PropertyMapBuilder().build(); private Map<String, String> properties = new HashMap<String, String>(); public PropertyMapBuilder() { this(null); } public PropertyMapBuilder(final Map<String, String> properties) { if (properties != null) { this.properties = new HashMap<String, String>(properties); } else { this.properties = new HashMap<String, String>(); } } public Map<String, String> build() { // Make a copy and return it return new HashMap<String, String>(properties); } public PropertyMapBuilder serverContext(final ServerContext context) { if (context != null) { final boolean isHosted = (context.getType() == ServerContext.Type.VSO) || (context.getType() == ServerContext.Type.VSO_DEPLOYMENT); add(TfsTelemetryConstants.SHARED_PROPERTY_IS_HOSTED, Boolean.toString(isHosted)); add(TfsTelemetryConstants.SHARED_PROPERTY_SERVER_ID, getServerId(context)); add(TfsTelemetryConstants.SHARED_PROPERTY_COLLECTION_ID, getCollectionId(context)); } return this; } public PropertyMapBuilder activeServerContext() { if (ServerContextManager.getInstance().getLastUsedContext() != null) { return serverContext(ServerContextManager.getInstance().getLastUsedContext()); } return this; } public PropertyMapBuilder currentOrActiveContext(final ServerContext context) { if (context != null) { return serverContext(context); } else { return activeServerContext(); } } public PropertyMapBuilder actionName(final String actionName) { if (!StringUtils.isEmpty(actionName)) { add(TfsTelemetryConstants.PLUGIN_EVENT_PROPERTY_COMMAND_NAME, actionName); } return this; } public PropertyMapBuilder success(final Boolean success) { if (success != null) { add(TfsTelemetryConstants.PLUGIN_EVENT_PROPERTY_IS_SUCCESS, success.toString()); } return this; } public PropertyMapBuilder message(final String message) { if (!StringUtils.isEmpty(message)) { add(TfsTelemetryConstants.PLUGIN_EVENT_PROPERTY_MESSAGE, message); } return this; } public PropertyMapBuilder pair(final String key, final String value) { if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(value)) { add(key, value); } return this; } private String getServerId(final ServerContext context) { if (context != null && context.getUri() != null) { return context.getUri().getHost(); } return UNKNOWN; } private String getCollectionId(final ServerContext context) { if (context != null && context.getTeamProjectCollectionReference() != null && context.getTeamProjectCollectionReference().getId() != null) { return context.getTeamProjectCollectionReference().getId().toString(); } return UNKNOWN; } private void add(final String key, final String value) { if (value != null) { // remove any newlines from the value field. Newlines currently cause the event to be lost in AppInsights properties.put(key, value.replace("\r", "").replace("\n", " ")); } else { properties.put(key, ""); //null values cause exceptions } } @Override public String toString() { return properties.toString(); } } }