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

Add support for connection string to Microsoft.ApplicationInsights.NLogTarget #2858

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.ApplicationIn
Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.ContextProperties.get -> System.Collections.Generic.IList<Microsoft.ApplicationInsights.NLogTarget.TargetPropertyWithContext>
Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.InstrumentationKey.get -> string
Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.InstrumentationKey.set -> void
Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.ConnectionString.get -> string
Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.ConnectionString.set -> void
Microsoft.ApplicationInsights.NLogTarget.TargetPropertyWithContext
Microsoft.ApplicationInsights.NLogTarget.TargetPropertyWithContext.Layout.get -> NLog.Layouts.Layout
Microsoft.ApplicationInsights.NLogTarget.TargetPropertyWithContext.Layout.set -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.ApplicationIn
Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.ContextProperties.get -> System.Collections.Generic.IList<Microsoft.ApplicationInsights.NLogTarget.TargetPropertyWithContext>
Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.InstrumentationKey.get -> string
Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.InstrumentationKey.set -> void
Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.ConnectionString.get -> string
Microsoft.ApplicationInsights.NLogTarget.ApplicationInsightsTarget.ConnectionString.set -> void
Microsoft.ApplicationInsights.NLogTarget.TargetPropertyWithContext
Microsoft.ApplicationInsights.NLogTarget.TargetPropertyWithContext.Layout.get -> NLog.Layouts.Layout
Microsoft.ApplicationInsights.NLogTarget.TargetPropertyWithContext.Layout.set -> void
Expand Down
28 changes: 27 additions & 1 deletion LOGGING/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,26 @@ Application Insights NLog Target nuget package adds ApplicationInsights target i

If your application does not have web.config then it can also be configured manually.

* **Configure ApplicationInsightsTarget using NLog.config** :
* **Configure ApplicationInsightsTarget with ConnectionString using NLog.config** :
ConnectionString is the preferred approach to write logs into Application Insights. If the NLog Application Insights target has connectionString in the config file then TelemetryClient will use it, otherwise it will use the instrumentationKey.

```xml
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<extensions>
<add assembly="Microsoft.ApplicationInsights.NLogTarget" />
</extensions>
<targets>
<target xsi:type="ApplicationInsightsTarget" name="aiTarget">
<connectionString>Your_ApplicationInsights_ConnectionString</connectionString> <!-- Only required if not using ApplicationInsights.config -->
<contextproperty name="threadid" layout="${threadid}" /> <!-- Can be repeated with more context -->
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="aiTarget" />
</rules>
</nlog>
```
* **Configure ApplicationInsightsTarget using NLog.config** :

```xml
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
Expand Down Expand Up @@ -70,6 +89,10 @@ For more information see:


```csharp
// You need this only if you did not define ConnectionString or InstrumentationKey in ApplicationInsights.config (Or in the NLog.config)
TelemetryConfiguration.Active.ConnectionString = "Your_ApplicationInsights_ConnectionString";

or
// You need this only if you did not define InstrumentationKey in ApplicationInsights.config (Or in the NLog.config)
TelemetryConfiguration.Active.InstrumentationKey = "Your_Resource_Key";

Expand All @@ -85,6 +108,9 @@ If you configure NLog programmatically with the [NLog Config API](https://github
var config = new LoggingConfiguration();

ApplicationInsightsTarget target = new ApplicationInsightsTarget();
// You need this only if you did not define Application Insights ConnectionString in ApplicationInsights.config or want to use different connectionstring
target.ConnectionString = "Your_ApplicationInsights_ConnectionString";
or
// You need this only if you did not define InstrumentationKey in ApplicationInsights.config or want to use different instrumentation key
target.InstrumentationKey = "Your_Resource_Key";

Expand Down
28 changes: 25 additions & 3 deletions LOGGING/src/NLogTarget/ApplicationInsightsTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace Microsoft.ApplicationInsights.NLogTarget

using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Extensibility.Implementation;
using Microsoft.ApplicationInsights.Implementation;

Expand All @@ -31,6 +32,7 @@ public sealed class ApplicationInsightsTarget : TargetWithLayout
private TelemetryClient telemetryClient;
private DateTime lastLogEventTime;
private NLog.Layouts.Layout instrumentationKeyLayout = string.Empty;
private NLog.Layouts.Layout connectionStringLayout = string.Empty;

/// <summary>
/// Initializers a new instance of ApplicationInsightsTarget type.
Expand All @@ -50,6 +52,15 @@ public string InstrumentationKey
set => this.instrumentationKeyLayout = value ?? string.Empty;
}

/// <summary>
/// Gets or sets the Application Insights connectionstring for your application.
/// </summary>
public string ConnectionString
{
get => (this.connectionStringLayout as NLog.Layouts.SimpleLayout)?.Text ?? null;
set => this.connectionStringLayout = value ?? string.Empty;
}

/// <summary>
/// Gets the array of custom attributes to be passed into the logevent context.
/// </summary>
Expand Down Expand Up @@ -118,10 +129,21 @@ protected override void InitializeTarget()
this.telemetryClient = new TelemetryClient();
Copy link
Contributor

@snakefoot snakefoot May 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe implement like this:

            string connectionString = this.connectionStringLayout.Render(LogEventInfo.CreateNullEvent());

            // Check if nlog application insights target has connectionstring in config file then
            // configure new telemetryclient with the connectionstring otherwise using legacy instrumentationkey.
            if (!string.IsNullOrWhiteSpace(connectionString))
            {
                var telemetryConfiguration = TelemetryConfiguration.CreateDefault();
                telemetryConfiguration.ConnectionString = connectionString;
                this.telemetryClient = new TelemetryClient(telemetryConfiguration);
            }
            else
            {
#pragma warning disable CS0618 // Type or member is obsolete: TelemtryConfiguration.Active is used in TelemetryClient constructor.
                this.telemetryClient = new TelemetryClient();
#pragma warning restore CS0618 // Type or member is obsolete
                string instrumentationKey = this.instrumentationKeyLayout.Render(LogEventInfo.CreateNullEvent());
                if (!string.IsNullOrWhiteSpace(instrumentationKey))
                {
                    this.telemetryClient.Context.InstrumentationKey = instrumentationKey;
                }
            }

This will resolve the issue about not to modify TelemtryConfiguration.Active

#pragma warning restore CS0618 // Type or member is obsolete

string instrumentationKey = this.instrumentationKeyLayout.Render(LogEventInfo.CreateNullEvent());
if (!string.IsNullOrWhiteSpace(instrumentationKey))
string connectionString = this.connectionStringLayout.Render(LogEventInfo.CreateNullEvent());

// Check if nlog application insights target has connectionstring in config file then
// configure telemetryclient with the connectionstring otherwise using instrumentationkey.
if (!string.IsNullOrWhiteSpace(connectionString))
{
this.telemetryClient.Context.InstrumentationKey = instrumentationKey;
this.telemetryClient.TelemetryConfiguration.ConnectionString = connectionString;
}
else
{
string instrumentationKey = this.instrumentationKeyLayout.Render(LogEventInfo.CreateNullEvent());
if (!string.IsNullOrWhiteSpace(instrumentationKey))
{
this.telemetryClient.Context.InstrumentationKey = instrumentationKey;
}
}

this.telemetryClient.Context.GetInternalContext().SdkVersion = SdkVersionUtils.GetSdkVersion("nlog:");
Expand Down
90 changes: 89 additions & 1 deletion LOGGING/test/NLogTarget.Tests/NLogTargetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public void TraceHasCustomProperties()
}


[TestMethod]
[TestMethod]
[TestCategory("NLogTarget")]
public void GlobalDiagnosticContextPropertiesAreAddedToProperties()
{
Expand Down Expand Up @@ -459,6 +459,62 @@ public void NLogTargetFlushesTelemetryClient()
Assert.AreEqual("Flush called", flushException.Message);
}

[TestMethod]
[TestCategory("NLogTarget")]
public void NLogInfoIsSentAsInformationTraceItemWithAIConnectionString()
{
var aiLogger = this.CreateTargetWithGivenConnectionString("Your_ApplicationInsights_ConnectionString");
aiLogger.Info("Info message");

var telemetry = (TraceTelemetry)this.adapterHelper.Channel.SentItems.First();
Assert.AreEqual($"Info message", telemetry.Message);
}

[TestMethod]
[TestCategory("NLogTarget")]
public void NLogTraceIsSentAsVerboseTraceItemWithAIConnectionString()
{
var aiLogger = this.CreateTargetWithGivenConnectionString("Your_ApplicationInsights_ConnectionString");
aiLogger.Trace("Trace message");

var telemetry = (TraceTelemetry)this.adapterHelper.Channel.SentItems.FirstOrDefault();
Assert.AreEqual("Trace message", telemetry.Message);
}

[TestMethod]
[TestCategory("NLogTarget")]
public void NLogDebugIsSentAsVerboseTraceItemWithAIConnectionString()
{
var aiLogger = this.CreateTargetWithGivenConnectionString("Your_ApplicationInsights_ConnectionString");
aiLogger.Debug("Debug Message");

var telemetry = (TraceTelemetry)this.adapterHelper.Channel.SentItems.FirstOrDefault();
Assert.AreEqual("Debug Message", telemetry.Message);
}

[TestMethod]
[TestCategory("NLogTarget")]
public void NLogWarnIsSentAsWarningTraceItemWithAIConnectionString()
{
var aiLogger = this.CreateTargetWithGivenConnectionString("Your_ApplicationInsights_ConnectionString");

aiLogger.Warn("Warn message");

var telemetry = (TraceTelemetry)this.adapterHelper.Channel.SentItems.FirstOrDefault();
Assert.AreEqual("Warn message", telemetry.Message);
}

[TestMethod]
[TestCategory("NLogTarget")]
public void NLogErrorIsSentAsVerboseTraceItemWithAIConnectionString()
{
var aiLogger = this.CreateTargetWithGivenConnectionString("InstrumentationKey=b91a8f48-c77c-4f12-80e2-f96bc1abb126;IngestionEndpoint=https://centralus-2.in.applicationinsights.azure.com/;LiveEndpoint=https://centralus.livediagnostics.monitor.azure.com/");
aiLogger.Error("Error Message");

var telemetry = (TraceTelemetry)this.adapterHelper.Channel.SentItems.FirstOrDefault();
Assert.AreEqual("Error Message", telemetry.Message);
}

private void VerifyMessagesInMockChannel(Logger aiLogger, string instrumentationKey)
{
aiLogger.Trace("Sample trace message");
Expand Down Expand Up @@ -504,5 +560,37 @@ private Logger CreateTargetWithGivenInstrumentationKey(

return aiLogger;
}

private Logger CreateTargetWithGivenConnectionString(
string connectionString = "Your_ApplicationInsights_ConnectionString",
Action<Logger> loggerAction = null)
{
// Mock channel to validate that our appender is trying to send logs
#pragma warning disable CS0618 // Type or member is obsolete
TelemetryConfiguration.Active.TelemetryChannel = this.adapterHelper.Channel;
#pragma warning restore CS0618 // Type or member is obsolete

ApplicationInsightsTarget target = new ApplicationInsightsTarget
{
ConnectionString = connectionString
};

LoggingRule rule = new LoggingRule("*", LogLevel.Trace, target);
LoggingConfiguration config = new LoggingConfiguration();
config.AddTarget("AITarget", target);
config.LoggingRules.Add(rule);

LogManager.Configuration = config;
Logger aiLogger = LogManager.GetLogger("AITarget");

if (loggerAction != null)
{
loggerAction(aiLogger);
target.Dispose();
return null;
}

return aiLogger;
}
}
}
9 changes: 7 additions & 2 deletions LOGGING/test/Shared/AdapterHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class AdapterHelper : IDisposable
{
public string InstrumentationKey { get; }

public string ConnectionString { get; }

#if NET452 || NET46
private static readonly string ApplicationInsightsConfigFilePath =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ApplicationInsights.config");
Expand All @@ -27,16 +29,19 @@ public class AdapterHelper : IDisposable
Path.Combine(Path.GetDirectoryName(typeof(AdapterHelper).GetTypeInfo().Assembly.Location), "ApplicationInsights.config");
#endif

public AdapterHelper(string instrumentationKey = "F8474271-D231-45B6-8DD4-D344C309AE69")
public AdapterHelper(string instrumentationKey = "F8474271-D231-45B6-8DD4-D344C309AE69"
, string connectionString = "Your_ApplicationInsights_ConnectionString")
{
this.InstrumentationKey = instrumentationKey;
this.ConnectionString = connectionString;

string configuration = string.Format(InvariantCulture,
@"<?xml version=""1.0"" encoding=""utf-8"" ?>
<ApplicationInsights xmlns=""http://schemas.microsoft.com/ApplicationInsights/2013/Settings"">
<InstrumentationKey>{0}</InstrumentationKey>
</ApplicationInsights>",
instrumentationKey);
instrumentationKey,
connectionString);

File.WriteAllText(ApplicationInsightsConfigFilePath, configuration);
this.Channel = new CustomTelemetryChannel();
Expand Down