/* * ApplicationInsights-Java * Copyright (c) Microsoft Corporation * All rights reserved. * * MIT License * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the ""Software""), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ package com.microsoft.applicationinsights; import java.util.Date; import java.util.Map; import com.microsoft.applicationinsights.common.CommonUtils; import com.microsoft.applicationinsights.extensibility.ContextInitializer; import com.microsoft.applicationinsights.extensibility.TelemetryInitializer; import com.microsoft.applicationinsights.extensibility.TelemetryProcessor; import com.microsoft.applicationinsights.extensibility.context.InternalContext; import com.microsoft.applicationinsights.internal.logger.InternalLogger; import com.microsoft.applicationinsights.internal.quickpulse.QuickPulseDataCollector; import com.microsoft.applicationinsights.internal.util.ChannelFetcher; import com.microsoft.applicationinsights.internal.shutdown.SDKShutdownActivity; import com.microsoft.applicationinsights.telemetry.*; import com.microsoft.applicationinsights.internal.util.MapUtil; import com.microsoft.applicationinsights.channel.TelemetryChannel; import com.google.common.base.Strings; // Created by gupele /** * Create an instance of this class to send telemetry to Azure Application Insights. * General overview https://docs.microsoft.com/azure/application-insights/app-insights-api-custom-events-metrics */ public class TelemetryClient { private final class TelemetryClientChannelFetcher implements ChannelFetcher { public TelemetryChannel fetch() { return getChannel(); } } private final TelemetryConfiguration configuration; private TelemetryContext context; private TelemetryChannel channel; private static final Object TELEMETRY_STOP_HOOK_LOCK = new Object(); private static final Object TELEMETRY_CONTEXT_LOCK = new Object(); /** * Initializes a new instance of the TelemetryClient class. Send telemetry with the specified configuration. * @param configuration The configuration this instance will work with. */ public TelemetryClient(TelemetryConfiguration configuration) { if (configuration == null) { configuration = TelemetryConfiguration.getActive(); } synchronized (TELEMETRY_STOP_HOOK_LOCK) { SDKShutdownActivity.INSTANCE.register(new TelemetryClientChannelFetcher()); } this.configuration = configuration; } /** * Initializes a new instance of the TelemetryClient class, configured from the active configuration. */ public TelemetryClient() { this(TelemetryConfiguration.getActive()); } /** * Gets the current context that is used to augment telemetry you send. * @return A telemetry context used for all records. Changes to it will impact all future telemetry in this * application session. */ public TelemetryContext getContext() { if (context == null) { // lock and recheck there is still no initialized context. If so, create one. synchronized (TELEMETRY_CONTEXT_LOCK) { if (context==null) { context = createInitializedContext(); } } } return context; } /** * Checks whether tracking is enabled. * @return 'true' if tracking is disabled, 'false' otherwise. */ public boolean isDisabled() { return Strings.isNullOrEmpty(getContext().getInstrumentationKey()) || configuration.isTrackingDisabled(); } /** * Sends the specified state of a user session to Application Insights. * @param sessionState {@link com.microsoft.applicationinsights.telemetry.SessionState} * value indicating the state of a user session. */ public void trackSessionState(SessionState sessionState) { this.track(new SessionStateTelemetry(sessionState)); } /** * Sends a custom event record to Application Insights. Appears in custom events in Analytics, Search and Metrics Explorer. * @param name A name for the event. Max length 150. * @param properties Named string values you can use to search and filter events. * @param metrics Numeric measurements associated with this event. Appear under Custom Metrics in Metrics Explorer. */ public void trackEvent(String name, Map<String, String> properties, Map<String, Double> metrics) { if (isDisabled()) { return; } if (Strings.isNullOrEmpty(name)) { name = ""; } EventTelemetry et = new EventTelemetry(name); if (properties != null && properties.size() > 0) { MapUtil.copy(properties, et.getContext().getProperties()); } if (metrics != null && metrics.size() > 0) { MapUtil.copy(metrics, et.getMetrics()); } this.track(et); } /** * Sends a custom event record to Application Insights. Appears in "custom events" in Analytics, Search and Metrics Explorer. * @param name A name for the event. Max length 150. */ public void trackEvent(String name) { trackEvent(name, null, null); } /** * Sends a custom event record to Application Insights. Appears in "custom events" in Analytics, Search and Metrics Explorer. * @param telemetry An event telemetry item. */ public void trackEvent(EventTelemetry telemetry) { track(telemetry); } /** * Sends a TraceTelemetry record to Application Insights. Appears in "traces" in Analytics and Search. * @param message A log message. Max length 10000. * @param severityLevel The severity level. * @param properties Named string values you can use to search and classify trace messages. */ public void trackTrace(String message, SeverityLevel severityLevel, Map<String, String> properties) { if (isDisabled()) { return; } if (Strings.isNullOrEmpty(message)) { message = ""; } TraceTelemetry et = new TraceTelemetry(message, severityLevel); if (properties != null && properties.size() > 0) { MapUtil.copy(properties, et.getContext().getProperties()); } this.track(et); } /** * Sends a TraceTelemetry record to Application Insights. Appears in "traces" in Analytics and Search. * @param message A log message. Max length 10000. */ public void trackTrace(String message) { trackTrace(message, null, null); } /** * Sends a TraceTelemetry record. Appears in "traces" in Analytics and Search. * @param message A log message. Max length 10000. * @param severityLevel The severity level. */ public void trackTrace(String message, SeverityLevel severityLevel) { trackTrace(message, severityLevel, null); } /** * Sends a TraceTelemetry record for display in Diagnostic Search. * @param telemetry The {@link com.microsoft.applicationinsights.telemetry.Telemetry} instance. */ public void trackTrace(TraceTelemetry telemetry) { this.track(telemetry); } /** * Sends a numeric metric to Application Insights. Appears in customMetrics in Analytics, and under Custom Metrics in Metric Explorer. * @param name The name of the metric. Max length 150. * @param value The value of the metric. Average if based on more than one sample count. Should be greater than 0. * @param sampleCount The sample count. * @param min The minimum value of the sample. * @param max The maximum value of the sample. * @param properties Named string values you can use to search and classify trace messages. */ public void trackMetric(String name, double value, int sampleCount, double min, double max, Map<String, String> properties) { if (isDisabled()) { return; } if (Strings.isNullOrEmpty(name)) { name = ""; } MetricTelemetry mt = new MetricTelemetry(name, value); mt.setCount(sampleCount); if (sampleCount > 1) { mt.setMin(min); mt.setMax(max); } if (properties != null && properties.size() > 0) { MapUtil.copy(properties, mt.getContext().getProperties()); } this.track(mt); } /** * Sends a numeric metric to Application Insights. Appears in customMetrics in Analytics, and under Custom Metrics in Metric Explorer. * @param name The name of the metric. Max length 150. * @param value The value of the metric. Should be greater than 0. */ public void trackMetric(String name, double value) { trackMetric(name, value, 1, value, value, null); } /** * Sends a numeric metric to Application Insights. Appears in customMetrics in Analytics, and under Custom Metrics in Metric Explorer. * @param telemetry The {@link com.microsoft.applicationinsights.telemetry.Telemetry} instance. */ public void trackMetric(MetricTelemetry telemetry) { track(telemetry); } /** * Sends an exception record to Application Insights. Appears in "exceptions" in Analytics and Search. * @param exception The exception to log information about. * @param properties Named string values you can use to search and classify trace messages. * @param metrics Measurements associated with this exception event. Appear in "custom metrics" in Metrics Explorer. */ public void trackException(Exception exception, Map<String, String> properties, Map<String, Double> metrics) { if (isDisabled()) { return; } ExceptionTelemetry et = new ExceptionTelemetry(exception); et.setExceptionHandledAt(ExceptionHandledAt.UserCode); if (properties != null && properties.size() > 0) { MapUtil.copy(properties, et.getContext().getProperties()); } if (metrics != null && metrics.size() > 0) { MapUtil.copy(metrics, et.getMetrics()); } this.track(et); } /** * Sends an exception record to Application Insights. Appears in "exceptions" in Analytics and Search. * @param exception The exception to log information about. */ public void trackException(Exception exception) { trackException(exception, null, null); } /** * Sends an ExceptionTelemetry record for display in Diagnostic Search. * @param telemetry An already constructed exception telemetry record. */ public void trackException(ExceptionTelemetry telemetry) { track(telemetry); } /** * Sends a request record to Application Insights. Appears in "requests" in Search and Analytics, * and contributes to metric charts such as Server Requests, Server Response Time, Failed Requests. * @param name A user-friendly name for the request or operation. * @param timestamp The time of the request. * @param duration The duration, in milliseconds, of the request processing. * @param responseCode The HTTP response code. * @param success true to record the operation as a successful request, false as a failed request. */ public void trackHttpRequest(String name, Date timestamp, long duration, String responseCode, boolean success) { if (isDisabled()) { return; } track(new RequestTelemetry(name, timestamp, duration, responseCode, success)); } /** * Sends a request record to Application Insights. Appears in "requests" in Search and Analytics, * and contributes to metric charts such as Server Requests, Server Response Time, Failed Requests. * * @param request request */ public void trackRequest(RequestTelemetry request) { track(request); } public void trackDependency(String dependencyName, String commandName, Duration duration, boolean success) { RemoteDependencyTelemetry remoteDependencyTelemetry = new RemoteDependencyTelemetry(dependencyName, commandName, duration, success); trackDependency(remoteDependencyTelemetry); } /** * Sends a dependency record to Application Insights. Appears in "dependencies" in Search and Analytics. * Set device type == "PC" to have the record contribute to metric charts such as * Server Dependency Calls, Dependency Response Time, and Dependency Failures. * @param telemetry telemetry */ public void trackDependency(RemoteDependencyTelemetry telemetry) { if (isDisabled()) { return; } if (telemetry == null) { telemetry = new RemoteDependencyTelemetry(""); } track(telemetry); } /** * Sends a page view record to Application Insights. Appears in "page views" in Search and Analytics, * and contributes to metric charts such as Page View Load Time. @param name The name of the page. */ public void trackPageView(String name) { // Avoid creation of data if not needed if (isDisabled()) { return; } if (name == null) { name = ""; } Telemetry telemetry = new PageViewTelemetry(name); track(telemetry); } /** * Send information about the page viewed in the application. * @param telemetry The telemetry to send */ public void trackPageView(PageViewTelemetry telemetry) { track(telemetry); } /** * This method is part of the Application Insights infrastructure. Do not call it directly. * @param telemetry The {@link com.microsoft.applicationinsights.telemetry.Telemetry} instance. */ public void track(Telemetry telemetry) { if (telemetry == null) { throw new IllegalArgumentException("telemetry item cannot be null"); } if (isDisabled()) { return; } telemetry.setTimestamp(new Date()); TelemetryContext ctx = this.getContext(); if (Strings.isNullOrEmpty(ctx.getInstrumentationKey())) { ctx.setInstrumentationKey(configuration.getInstrumentationKey()); } try { telemetry.getContext().initialize(ctx); } catch (Throwable t) { InternalLogger.INSTANCE.error("Exception while telemetry context's initialization: '%s'", t.getMessage()); } activateInitializers(telemetry); if (Strings.isNullOrEmpty(telemetry.getContext().getInstrumentationKey())) { throw new IllegalArgumentException("Instrumentation key cannot be undefined."); } try { telemetry.sanitize(); } catch (Throwable t) { InternalLogger.INSTANCE.error("Exception while sanitizing telemetry: '%s'",t.getMessage()); } if (!activateProcessors(telemetry)) { return; } try { QuickPulseDataCollector.INSTANCE.add(telemetry); } catch (Throwable t) { } try { getChannel().send(telemetry); } catch (Throwable t) { InternalLogger.INSTANCE.error("Exception while sending telemetry: '%s'",t.getMessage()); } } private void activateInitializers(Telemetry telemetry) { for (TelemetryInitializer initializer : this.configuration.getTelemetryInitializers()) { try { initializer.initialize(telemetry); } catch (Throwable e) { InternalLogger.INSTANCE.error("Failed during telemetry initialization class '%s', exception: %s", initializer.getClass().getName(), e.getMessage()); } } } private boolean activateProcessors(Telemetry telemetry) { for (TelemetryProcessor processor : configuration.getTelemetryProcessors()) { try { if (!processor.process(telemetry)) { return false; } } catch (Throwable t) { InternalLogger.INSTANCE.error("Exception while processing telemetry: '%s'",t.getMessage()); } } return true; } /** * Flushes possible pending Telemetries in the channel. Not required for a continuously-running server application. */ public void flush() { getChannel().flush(); } /** * Gets the channel used by the client. */ TelemetryChannel getChannel() { if (channel == null) { this.channel = configuration.getChannel(); } return this.channel; } private TelemetryContext createInitializedContext() { TelemetryContext ctx = new TelemetryContext(); ctx.setInstrumentationKey(configuration.getInstrumentationKey()); for (ContextInitializer init : configuration.getContextInitializers()) { try { init.initialize(ctx); } catch (Throwable t) { InternalLogger.INSTANCE.error("Exception in context initializer: '%s'", t.getMessage()); } } // Set the nodeName for billing purpose if it does not already exist InternalContext internal = ctx.getInternal(); if (CommonUtils.isNullOrEmpty(internal.getNodeName())) { String host = CommonUtils.getHostName(); if (!CommonUtils.isNullOrEmpty(host)) { internal.setNodeName(host); } } return ctx; } }