diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0434444
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,256 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+/src/Tests/coverage.json
+/src/Tests/coverage.opencover.xml
+/src/ConsoleApp1
+/src/WebApplication1
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6da9e30
--- /dev/null
+++ b/README.md
@@ -0,0 +1,49 @@
+# **Cloud.Core.Telemetry.AzureAppInsights**
+
+
+
+Azure specific implementation of telemetry logging. Uses the ITelemeteryLogger interface from _Cloud.Core_ and can be used in conjunction with the Telemetry Logging
+Provider classes found in the _Cloud.Core.Telemetry.Logging_ package.
+
+
+
+## Usage
+
+As the logger follows the base `ILogger` implementation, it's very flexible in how it can be used. To create an instance, do the following:
+
+```
+var logger = new AppInsightsLogger("insightsKey");
+
+logger.LogInformation("Sample information message");
+logger.LogWarning("Sample warning message");
+logger.LogDebug("Sample debug message");
+logger.LogException("Sample exception message");
+```
+
+Any of the logging methods can also handle exception, such as:
+
+```
+logger.LogWarning(new Exception("Something's gone wrong!"));
+``
+
+## Test Coverage
+A threshold will be added to this package to ensure the test coverage is above 80% for branches, functions and lines. If it's not above the required threshold
+(threshold that will be implemented on ALL of the new core repositories going forward), then the build will fail.
+
+## Compatibility
+This package has has been written in .net Standard and can be therefore be referenced from a .net Core or .net Framework application. The advantage of utilising from a .net Core application,
+is that it can be deployed and run on a number of host operating systems, such as Windows, Linux or OSX. Unlike referencing from the a .net Framework application, which can only run on
+Windows (or Linux using Mono).
+
+## Setup
+This package requires the .net Core 2.1 SDK, it can be downloaded here:
+https://www.microsoft.com/net/download/dotnet-core/2.1
+
+IDE of Visual Studio or Visual Studio Code, can be downloaded here:
+https://visualstudio.microsoft.com/downloads/
+
+## How to access this package
+All of the Cloud.Core.* packages are published to our internal NuGet feed. To consume this on your local development machine, please add the following feed to your feed sources in Visual Studio:
+TBC
+
+For help setting up, follow this article: https://docs.microsoft.com/en-us/vsts/package/nuget/consume?view=vsts
diff --git a/src/Cloud.Core.Telemetry.AzureAppInsights.sln b/src/Cloud.Core.Telemetry.AzureAppInsights.sln
new file mode 100644
index 0000000..1144336
--- /dev/null
+++ b/src/Cloud.Core.Telemetry.AzureAppInsights.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27703.2047
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cloud.Core.Telemetry.AzureAppInsights", "Cloud.Core.Telemetry.AzureAppInsights\Cloud.Core.Telemetry.AzureAppInsights.csproj", "{49353CB1-6857-4E9E-B65C-A9A894025902}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cloud.Core.Telemetry.AzureAppInsights.Tests", "Tests\Cloud.Core.Telemetry.AzureAppInsights.Tests.csproj", "{F0CD7CC0-BDAB-4AA8-8C5B-0D5829C6820F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {49353CB1-6857-4E9E-B65C-A9A894025902}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {49353CB1-6857-4E9E-B65C-A9A894025902}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {49353CB1-6857-4E9E-B65C-A9A894025902}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {49353CB1-6857-4E9E-B65C-A9A894025902}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F0CD7CC0-BDAB-4AA8-8C5B-0D5829C6820F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F0CD7CC0-BDAB-4AA8-8C5B-0D5829C6820F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F0CD7CC0-BDAB-4AA8-8C5B-0D5829C6820F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F0CD7CC0-BDAB-4AA8-8C5B-0D5829C6820F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {D03DCC35-27ED-48CE-ADFA-5D0537FB20FF}
+ EndGlobalSection
+EndGlobal
diff --git a/src/Cloud.Core.Telemetry.AzureAppInsights.sln.DotSettings b/src/Cloud.Core.Telemetry.AzureAppInsights.sln.DotSettings
new file mode 100644
index 0000000..cb1f1a6
--- /dev/null
+++ b/src/Cloud.Core.Telemetry.AzureAppInsights.sln.DotSettings
@@ -0,0 +1,22 @@
+
+ SUGGESTION
+ SUGGESTION
+ SUGGESTION
+ SUGGESTION
+ SUGGESTION
+ True
+ DO_NOT_SHOW
+ SUGGESTION
+ SUGGESTION
+ SUGGESTION
+ SUGGESTION
+ SUGGESTION
+ <Configurator><ConnectList /></Configurator>
+ True
+
+ True
+ 1
+ True
+ True
+
+
\ No newline at end of file
diff --git a/src/Cloud.Core.Telemetry.AzureAppInsights/AppInsights.cs b/src/Cloud.Core.Telemetry.AzureAppInsights/AppInsights.cs
new file mode 100644
index 0000000..52e3061
--- /dev/null
+++ b/src/Cloud.Core.Telemetry.AzureAppInsights/AppInsights.cs
@@ -0,0 +1,361 @@
+namespace Cloud.Core.Telemetry.AzureAppInsights
+{
+ using Diagnostics = System.Diagnostics;
+ using System;
+ using Microsoft.Extensions.Logging;
+ using System.Collections.Generic;
+ using Microsoft.ApplicationInsights.DataContracts;
+ using Microsoft.ApplicationInsights.Extensibility;
+ using Microsoft.ApplicationInsights;
+ using System.Runtime.CompilerServices;
+
+ ///
+ /// Azure specific Application Insights implementation.
+ ///
+ ///
+ public class AppInsightsLogger : ITelemetryLogger
+ {
+ private readonly string _instrumentationKey;
+ private readonly LogLevel _level;
+
+ private TelemetryClient _client;
+
+ public string InstrumentationKey => _instrumentationKey;
+ public LogLevel LogLevel => _level;
+
+ internal TelemetryClient Client
+ {
+ get
+ {
+ if (_client == null)
+ {
+ // Initialise client.
+ TelemetryConfiguration.Active.InstrumentationKey = _instrumentationKey;
+ _client = new TelemetryClient();
+
+ // Default application information - who's running the code, guid for session id and operating system being executed on.
+ Client.Context.User.Id = AppDomain.CurrentDomain.FriendlyName;
+ Client.Context.Session.Id = Guid.NewGuid().ToString();
+ Client.Context.Device.OperatingSystem = Environment.OSVersion.ToString();
+ }
+
+ return _client;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AppInsightsLogger(string instrumentationKey, LogLevel level)
+ {
+ _instrumentationKey = instrumentationKey;
+ _level = level;
+ }
+
+ ///
+ public void Flush()
+ {
+ Client.Flush();
+ }
+
+ ///
+ /// Checks if the given is enabled.
+ ///
+ /// level to be checked.
+ ///
+ /// true if enabled.
+ ///
+ public bool IsEnabled(LogLevel logLevel)
+ {
+ return logLevel >= _level;
+ }
+
+ ///
+ /// Begins the scope.
+ ///
+ ///
+ /// The state.
+ ///
+ public IDisposable BeginScope(T state)
+ {
+ return null;
+ }
+
+ ///
+ public void Log(LogLevel logLevel, EventId eventId, T state, Exception exception,
+ Func formatter)
+ {
+ if (!IsEnabled(logLevel))
+ return;
+
+ var message = formatter != null ? formatter(state, exception) : state.ToString();
+
+ if (string.IsNullOrEmpty(eventId.Name))
+ {
+ var frame = new Diagnostics.StackFrame(5); // Default stack level which equates to the actual calling code...
+
+ var method = frame.GetMethod();
+
+ if (method.IsNullOrDefault())
+ {
+ frame = new Diagnostics.StackFrame(4);
+ method = frame.GetMethod();
+ }
+
+ var type = method.DeclaringType;
+ var name = method.Name;
+
+ eventId = new EventId(
+ eventId.Id == 0 ? BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0) : eventId.Id,
+ $"{type}:{name}");
+ }
+
+ var props = new Dictionary
+ {
+ {"Telemetry.EventId", eventId.Id.ToString()},
+ {"Telemetry.EventName", eventId.Name}
+ };
+
+ if (exception != null)
+ {
+ CreateTelemetryException(logLevel, exception, props, string.Empty, string.Empty, -1);
+ }
+ else
+ {
+ CreateTelemetryEvent(logLevel, message, props, string.Empty, string.Empty, -1);
+ }
+ }
+
+ ///
+ public void LogVerbose(string message, Dictionary properties = null,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryEvent(LogLevel.Information, message, properties, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ ///
+ public void LogInformation(string message, Dictionary properties = null,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryEvent(LogLevel.Information, message, properties, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ ///
+ public void LogCritical(string message, Dictionary properties = null,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryEvent(LogLevel.Information, message, properties, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ ///
+ public void LogCritical(Exception ex, Dictionary properties = null,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryException(LogLevel.Critical, ex, properties, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ /// Logs the debug.
+ /// The message.
+ /// The properties.
+ /// Name of the caller member.
+ /// The caller file path.
+ /// The caller line number.
+ public void LogDebug(string message, Dictionary properties = null,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryEvent(LogLevel.Debug, message, properties, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ /// Logs the debug.
+ /// The ex.
+ /// The properties.
+ /// Name of the caller member.
+ /// The caller file path.
+ /// The caller line number.
+ public void LogDebug(Exception ex, Dictionary properties = null,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryException(LogLevel.Debug, ex, properties, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ ///
+ public void LogWarning(string message, Dictionary properties = null,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryEvent(LogLevel.Warning, message, properties, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ ///
+ public void LogWarning(Exception ex, Dictionary properties = null,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryException(LogLevel.Warning, ex, properties, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ ///
+ public void LogError(string message, Dictionary properties = null,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryEvent(LogLevel.Error, message, properties, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ ///
+ public void LogError(Exception ex, Dictionary properties = null,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryException(LogLevel.Error, ex, properties, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ ///
+ public void LogMetric(string metricName, double metricValue,
+ [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "",
+ [CallerLineNumber] int callerLineNumber = -1)
+ {
+ CreateTelemetryMetric(LogLevel.Information, metricName, metricValue, null, callerMemberName, callerFilePath,
+ callerLineNumber);
+ }
+
+ ///
+ /// Sets the default properties.
+ ///
+ /// The log level.
+ /// The properties.
+ /// Name of the caller member.
+ /// The caller file path.
+ /// The caller line number.
+ ///
+ private Dictionary SetDefaultProperties(LogLevel logLevel,
+ Dictionary properties,
+ string callerMemberName, string callerFilePath, int callerLineNumber)
+ {
+ // If properties were not set, initialise now and add default properties.
+ var output = properties == null ? new Dictionary() : new Dictionary(properties);
+
+ output.Add("Telemetry.LogLevel", logLevel.ToString());
+
+ if (!string.IsNullOrEmpty(callerMemberName) && !output.ContainsKey("Telemetry.SummaryMessage"))
+ output.Add("Telemetry.MemberName", callerMemberName);
+
+ if (!string.IsNullOrEmpty(callerFilePath) && !output.ContainsKey("Telemetry.SummaryMessage"))
+ output.Add("Telemetry.FilePath", System.IO.Path.GetFileName(callerFilePath));
+
+ if (callerLineNumber > 0 && !output.ContainsKey("Telemetry.SummaryMessage"))
+ output.Add("Telemetry.LineNumber", callerLineNumber.ToString());
+
+ return output;
+ }
+
+ /// Creates the telemetry event.
+ /// The level.
+ /// The message.
+ /// The properties.
+ /// Name of the caller member.
+ /// The caller file path.
+ /// The caller line number.
+ private void CreateTelemetryEvent(LogLevel level, string message, Dictionary properties,
+ string callerMemberName, string callerFilePath, int callerLineNumber)
+ {
+ if (!IsEnabled(level))
+ return;
+
+ var telemetry = new EventTelemetry(message);
+
+ var output = SetDefaultProperties(level, properties, callerMemberName, callerFilePath, callerLineNumber);
+
+ if (!string.IsNullOrEmpty(message) && !telemetry.Properties.ContainsKey("Telemetry.SummaryMessage"))
+ telemetry.Properties.Add("Telemetry.SummaryMessage", message);
+
+ foreach (var property in output)
+ {
+ if (!telemetry.Properties.ContainsKey(property.Key))
+ telemetry.Properties.Add(property);
+ }
+
+ Client.TrackEvent(telemetry);
+ }
+
+ ///
+ /// Creates the telemetry exception.
+ ///
+ /// The level.
+ /// The ex.
+ /// The properties.
+ /// Name of the caller member.
+ /// The caller file path.
+ /// The caller line number.
+ private void CreateTelemetryException(LogLevel level, Exception ex, Dictionary properties,
+ string callerMemberName, string callerFilePath, int callerLineNumber)
+ {
+ if (!IsEnabled(level))
+ return;
+
+ var telemetry = new ExceptionTelemetry(ex);
+
+ var output = SetDefaultProperties(level, properties, callerMemberName, callerFilePath, callerLineNumber);
+
+ if (!string.IsNullOrEmpty(ex.Message) && !telemetry.Properties.ContainsKey("Telemetry.ExceptionMessage"))
+ telemetry.Properties.Add("Telemetry.ExceptionMessage", ex.Message);
+
+ foreach (var property in output)
+ {
+ if (!telemetry.Properties.ContainsKey(property.Key))
+ telemetry.Properties.Add(property);
+ }
+
+ Client.TrackException(telemetry);
+ }
+
+ ///
+ /// Creates the telemetry metric.
+ ///
+ /// The level.
+ /// Name of the metric.
+ /// The metric value.
+ /// The properties.
+ /// Name of the caller member.
+ /// The caller file path.
+ /// The caller line number.
+ private void CreateTelemetryMetric(LogLevel level, string metricName, double metricValue,
+ Dictionary properties,
+ string callerMemberName, string callerFilePath, int callerLineNumber)
+ {
+ if (!IsEnabled(level))
+ return;
+
+ var telemetry = new MetricTelemetry(metricName, metricValue);
+
+ var output = SetDefaultProperties(level, properties, callerMemberName, callerFilePath, callerLineNumber);
+
+ if (!string.IsNullOrEmpty(metricName) && !telemetry.Properties.ContainsKey("Telemetry.Metric"))
+ telemetry.Properties.Add("Telemetry.Metric", metricName);
+
+ foreach (var property in output)
+ {
+ if (!telemetry.Properties.ContainsKey(property.Key))
+ telemetry.Properties.Add(property);
+ }
+
+ Client.TrackMetric(telemetry);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Cloud.Core.Telemetry.AzureAppInsights/AssemblyInfo.cs b/src/Cloud.Core.Telemetry.AzureAppInsights/AssemblyInfo.cs
new file mode 100644
index 0000000..2ec5797
--- /dev/null
+++ b/src/Cloud.Core.Telemetry.AzureAppInsights/AssemblyInfo.cs
@@ -0,0 +1,4 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Cloud.Core.Telemetry.AzureAppInsights.Tests")]
+[assembly: InternalsVisibleTo("Cloud.Core.Telemetry.AzureAppInsights.Tests.Profiler")]
\ No newline at end of file
diff --git a/src/Cloud.Core.Telemetry.AzureAppInsights/Cloud.Core.Telemetry.AzureAppInsights.csproj b/src/Cloud.Core.Telemetry.AzureAppInsights/Cloud.Core.Telemetry.AzureAppInsights.csproj
new file mode 100644
index 0000000..b11801a
--- /dev/null
+++ b/src/Cloud.Core.Telemetry.AzureAppInsights/Cloud.Core.Telemetry.AzureAppInsights.csproj
@@ -0,0 +1,38 @@
+
+
+
+ netstandard2.0
+ Robert McCabe
+ true
+ Cloud.Core.Telemetry.AzureAppInsights
+ false
+ Cloud.Core.Telemetry.AzureAppInsights AppInsights Azure .net-Core Core Logging
+
+ Azure specific implementation of ITelemetryLogger using AppInsights.
+ Cloud.Core.Telemetry.AzureAppInsights
+ 1.0
+ Robert McCabe
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\..\..\.nuget\packages\newtonsoft.json\11.0.2\lib\netstandard2.0\Newtonsoft.Json.dll
+
+
+ ..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7\System.Configuration.dll
+
+
+
+
+
+
+
+
diff --git a/src/Cloud.Core.Telemetry.AzureAppInsights/Extensions/LoggingBuilderExtensions.cs b/src/Cloud.Core.Telemetry.AzureAppInsights/Extensions/LoggingBuilderExtensions.cs
new file mode 100644
index 0000000..17eba81
--- /dev/null
+++ b/src/Cloud.Core.Telemetry.AzureAppInsights/Extensions/LoggingBuilderExtensions.cs
@@ -0,0 +1,54 @@
+namespace Microsoft.Extensions.Logging
+{
+ using System;
+ using Cloud.Core;
+ using Cloud.Core.Telemetry.AzureAppInsights;
+ using Cloud.Core.Telemetry.Logging;
+ using Microsoft.Extensions.Configuration;
+ using DependencyInjection;
+
+ ///
+ /// Telemetry logger factory adds the AddTelemetryLogger method to the ILoggingBuilder.
+ ///
+ public static class LoggingBuilderExtensions
+ {
+ ///
+ /// Adds the telemetry logger factory method to the logging builder.
+ ///
+ /// The builder to add to.
+ /// The modified builder.
+ public static ILoggingBuilder AddAppInsightsLogger(this ILoggingBuilder builder)
+ {
+ builder.Services.AddSingleton((a) =>
+ {
+ var config = a.GetService();
+
+ var key = config.GetValue("Logging:InstrumentationKey");
+ var defaultLevel = config.GetValue("Logging:LogLevel:Default");
+ var telemetryLevel = config.GetValue("Logging:LogLevel:Telemetry");
+
+ return new AppInsightsLogger(key, GetLogLevel(defaultLevel, telemetryLevel));
+ });
+
+ builder.Services.AddSingleton(a => new TelemetryLoggerProvider(a.GetService()));
+ return builder;
+ }
+
+ private static LogLevel GetLogLevel(string defaultLevel, string desiredLevel)
+ {
+ if (!desiredLevel.IsNullOrEmpty())
+ defaultLevel = desiredLevel;
+
+ switch (defaultLevel)
+ {
+ case "Trace": return LogLevel.Trace;
+ case "Information": return LogLevel.Information;
+ case "Critical": return LogLevel.Critical;
+ case "Debug": return LogLevel.Debug;
+ case "Error": return LogLevel.Error;
+ case "Warning": return LogLevel.Warning;
+ default: return LogLevel.None;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Tests/AppInsightsTest.cs b/src/Tests/AppInsightsTest.cs
new file mode 100644
index 0000000..096ec53
--- /dev/null
+++ b/src/Tests/AppInsightsTest.cs
@@ -0,0 +1,450 @@
+using System;
+using System.Collections.Generic;
+using Cloud.Core.Testing;
+using FluentAssertions;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Xunit;
+
+namespace Cloud.Core.Telemetry.AzureAppInsights.Tests.Unit
+{
+ public class AppInsightsTest
+ {
+ [Fact, IsUnit]
+ public void Test_Telemetry_IsEnabled()
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.BeginScope("test");
+ logger.Flush();
+ Assert.True(logger.IsEnabled(LogLevel.Information));
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogWarningMessage()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogWarning("test");
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogWithWrongLevel()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Warning);
+ logger.LogDebug("test");
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogCriticalMessage()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogCritical("test");
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogInformationMessage()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+
+
+ Dictionary statistics = new Dictionary()
+ {
+ {"SomeProp1", "sample1"},
+ {"SomeProp2", "sample2"},
+ {"SomeProp3", "sample3" }
+ };
+
+ logger.LogInformation("test title", statistics);
+ logger.Log(LogLevel.Information, new EventId(1, null), "A", null, (a, b) =>
+ {
+ return string.Format("{0}:{1}", a, b);
+ });
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogVerboseMessage()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogVerbose("test");
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogErrorMessage()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogError("test");
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogWarningDebug()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogDebug(new Exception());
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogWarningException()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogWarning(new Exception());
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LoCriticalException()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogCritical(new Exception());
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogExceptionWithMessage()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogError(new Exception(), "Some error");
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_ExceptionWithMessageObject()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogError(new Exception(), "test", new Dictionary { { "a", "a" } });
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_ExceptionWithMessageFullParams()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogError(new Exception(), "test", new Dictionary { { "a", "a" } }, "", "", 1);
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_ExceptionWithDictionary()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogError(new Exception(), new Dictionary { { "a", "a" } });
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogLevelDebug()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Debug);
+ logger.Log(LogLevel.Debug, new EventId(1, "test"), "test", new Exception("test"), null);
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogLevelError()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Error);
+ logger.Log(LogLevel.Error, new EventId(1, "test"), "test", new Exception("test"), null);
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogLevelInfo()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Information);
+ logger.Log(LogLevel.Information, new EventId(1, "test"), "test", null, null);
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogLevelInfoWithException()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Information);
+ logger.Log(LogLevel.Error, new EventId(1, "test"), "test", new Exception("test"), null);
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogLevelInfoWithEvent()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Information);
+ logger.Log(LogLevel.Information, new EventId(1, "test"), "test", null, null);
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogLevelNoneWithException()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.None);
+ logger.Log(LogLevel.Trace, new EventId(1, "test"), "test", new Exception(), null);
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogLevelNone()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.None);
+ logger.Log(LogLevel.Trace, new EventId(1, "test"), "test", null, null);
+ logger.LogError(new Exception(), null, null);
+ logger.LogMetric("test", Double.MinValue);
+ logger.Log(LogLevel.Trace, new EventId(1, "test"), "test", null, null);
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_Telemetry_LogMetric()
+ {
+ AssertExtensions.DoesNotThrow(() =>
+ {
+ var logger = new AppInsightsLogger("test", LogLevel.Trace);
+ logger.LogMetric("test", 3.1);
+ logger.Flush();
+ });
+ }
+
+ [Fact, IsUnit]
+ public void Test_BuilderExtension_Critical()
+ {
+ IConfiguration config = new ConfigurationBuilder().AddInMemoryCollection(new List>()
+ {
+ new KeyValuePair("Logging:Instrumentationkey", "test"),
+ new KeyValuePair("Logging:LogLevel:Default", "Critical")
+
+ }).Build();
+
+ IServiceCollection collection = new ServiceCollection()
+ .AddSingleton(config)
+ .AddLogging(builder =>
+ {
+ builder.AddConfiguration(config);
+ builder.AddAppInsightsLogger();
+ });
+
+ var prov = collection.BuildServiceProvider();
+
+ var logger = prov.GetService() as AppInsightsLogger;
+ var logProvider = prov.GetService();
+
+ logProvider.Should().NotBeNull();
+ logger.Should().NotBeNull();
+ logger.LogLevel.Should().Be(LogLevel.Critical);
+ logger.InstrumentationKey.Should().Be("test");
+ }
+
+ [Fact, IsUnit]
+ public void Test_BuilderExtension_Debug()
+ {
+ IConfiguration config = new ConfigurationBuilder().AddInMemoryCollection(new List>()
+ {
+ new KeyValuePair("Logging:Instrumentationkey", "test"),
+ new KeyValuePair("Logging:LogLevel:Default", "Information"),
+ new KeyValuePair("Logging:LogLevel:Telemetry", "Debug"),
+
+ }).Build();
+
+ IServiceCollection collection = new ServiceCollection()
+ .AddSingleton(config)
+ .AddLogging(builder =>
+ {
+ builder.AddConfiguration(config);
+ builder.AddAppInsightsLogger();
+ });
+
+ var logger = collection.BuildServiceProvider().GetService() as AppInsightsLogger;
+
+ logger.Should().NotBeNull();
+ logger.LogLevel.Should().Be(LogLevel.Debug);
+ logger.InstrumentationKey.Should().Be("test");
+ }
+
+ [Fact, IsUnit]
+ public void Test_BuilderExtension_Information()
+ {
+ IConfiguration config = new ConfigurationBuilder().AddInMemoryCollection(new List>()
+ {
+ new KeyValuePair("Logging:Instrumentationkey", "test"),
+ new KeyValuePair("Logging:LogLevel:Default", "Information"),
+
+ }).Build();
+
+ IServiceCollection collection = new ServiceCollection()
+ .AddSingleton(config)
+ .AddLogging(builder =>
+ {
+ builder.AddConfiguration(config);
+ builder.AddAppInsightsLogger();
+ });
+
+ var logger = collection.BuildServiceProvider().GetService() as AppInsightsLogger;
+
+ logger.Should().NotBeNull();
+ logger.LogLevel.Should().Be(LogLevel.Information);
+ logger.InstrumentationKey.Should().Be("test");
+ }
+
+ [Fact, IsUnit]
+ public void Test_BuilderExtension_Warning()
+ {
+ IConfiguration config = new ConfigurationBuilder().AddInMemoryCollection(new List>()
+ {
+ new KeyValuePair("Logging:Instrumentationkey", "test"),
+ new KeyValuePair("Logging:LogLevel:Default", "Warning"),
+
+ }).Build();
+
+ IServiceCollection collection = new ServiceCollection()
+ .AddSingleton(config)
+ .AddLogging(builder =>
+ {
+ builder.AddConfiguration(config);
+ builder.AddAppInsightsLogger();
+ });
+
+ var logger = collection.BuildServiceProvider().GetService() as AppInsightsLogger;
+
+ logger.Should().NotBeNull();
+ logger.LogLevel.Should().Be(LogLevel.Warning);
+ logger.InstrumentationKey.Should().Be("test");
+ }
+
+ [Fact, IsUnit]
+ public void Test_BuilderExtension_Error()
+ {
+ IConfiguration config = new ConfigurationBuilder().AddInMemoryCollection(new List>()
+ {
+ new KeyValuePair("Logging:Instrumentationkey", "test"),
+ new KeyValuePair("Logging:LogLevel:Default", "Error"),
+
+ }).Build();
+
+ IServiceCollection collection = new ServiceCollection()
+ .AddSingleton(config)
+ .AddLogging(builder =>
+ {
+ builder.AddConfiguration(config);
+ builder.AddAppInsightsLogger();
+ });
+
+ var logger = collection.BuildServiceProvider().GetService() as AppInsightsLogger;
+
+ logger.Should().NotBeNull();
+ logger.LogLevel.Should().Be(LogLevel.Error);
+ logger.InstrumentationKey.Should().Be("test");
+ }
+
+ [Fact, IsUnit]
+ public void Test_BuilderExtension_None()
+ {
+ IConfiguration config = new ConfigurationBuilder().AddInMemoryCollection(new List>()
+ {
+ new KeyValuePair("Logging:Instrumentationkey", "test"),
+ new KeyValuePair("Logging:LogLevel:Default", "None"),
+
+ }).Build();
+
+ IServiceCollection collection = new ServiceCollection()
+ .AddSingleton(config)
+ .AddLogging(builder =>
+ {
+ builder.AddConfiguration(config);
+ builder.AddAppInsightsLogger();
+ });
+
+ var logger = collection.BuildServiceProvider().GetService() as AppInsightsLogger;
+
+ logger.Should().NotBeNull();
+ logger.LogLevel.Should().Be(LogLevel.None);
+ logger.InstrumentationKey.Should().Be("test");
+ }
+
+ [Fact, IsUnit]
+ public void Test_BuilderExtension_Trace()
+ {
+ IConfiguration config = new ConfigurationBuilder().AddInMemoryCollection(new List>()
+ {
+ new KeyValuePair("Logging:Instrumentationkey", "test"),
+ new KeyValuePair("Logging:LogLevel:Default", "Trace"),
+
+ }).Build();
+
+ IServiceCollection collection = new ServiceCollection()
+ .AddSingleton(config)
+ .AddLogging(builder =>
+ {
+ builder.AddConfiguration(config);
+ builder.AddAppInsightsLogger();
+ });
+
+ var logger = collection.BuildServiceProvider().GetService() as AppInsightsLogger;
+
+ logger.Should().NotBeNull();
+ logger.LogLevel.Should().Be(LogLevel.Trace);
+ logger.InstrumentationKey.Should().Be("test");
+ }
+ }
+}
diff --git a/src/Tests/Cloud.Core.Telemetry.AzureAppInsights.Tests.csproj b/src/Tests/Cloud.Core.Telemetry.AzureAppInsights.Tests.csproj
new file mode 100644
index 0000000..27b41b4
--- /dev/null
+++ b/src/Tests/Cloud.Core.Telemetry.AzureAppInsights.Tests.csproj
@@ -0,0 +1,33 @@
+
+
+
+ netcoreapp2.1
+ false
+ Robert McCabe
+ Robert McCabe
+ Unit tests for Cloud.Core.Telemetry.AzureAppInsights package.
+ Robert McCabe
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
+
+
+
+
+
+
+
+
+