Skip to content

Commit

Permalink
NLog EcsLayout apply default service details like Environment
Browse files Browse the repository at this point in the history
  • Loading branch information
snakefoot committed Jul 1, 2024
1 parent 32f5f1c commit 7a51998
Showing 1 changed file with 123 additions and 74 deletions.
197 changes: 123 additions & 74 deletions src/Elastic.CommonSchema.NLog/EcsLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Elastic.CommonSchema.NLog
internal class NlogEcsDocumentCreationOptions : IEcsDocumentCreationOptions
{
public static NlogEcsDocumentCreationOptions Default { get; } = new();
public bool IncludeHost { get; set; } = false;
public bool IncludeHost { get; set; } = true;
public bool IncludeProcess { get; set; } = false;
public bool IncludeUser { get; set; } = false;
public bool IncludeActivityData { get; set; } = false;
Expand All @@ -33,9 +33,13 @@ public class EcsLayout : Layout
{
/// <summary> An NLOG layout implementation that renders logs as ECS json</summary>
public const string Name = nameof(EcsLayout);

private static bool? _nlogApmLoaded;
private static Agent _defaultAgent;
private Agent _previousAgent;
private Service _previousService;
private Host _previousHost;
private Server _previousServer;
private Process _previousProcess;

private static bool NLogApmLoaded()
{
Expand Down Expand Up @@ -160,6 +164,8 @@ private static bool NLogWeb4Registered() =>
public Layout ApmServiceNodeName { get; set; }
/// <inheritdoc cref="ServiceFieldSet.Version"/>
public Layout ApmServiceVersion { get; set; }
/// <inheritdoc cref="ServiceFieldSet.Environment"/>
public Layout ServiceEnvironment { get; set; }

/// <inheritdoc cref="LogFieldSet.OriginFunction"/>
public Layout LogOriginCallSiteMethod { get; set; }
Expand Down Expand Up @@ -237,6 +243,8 @@ private static bool NLogWeb4Registered() =>

/// <inheritdoc cref="ServerFieldSet.Address"/>
public Layout ServerAddress { get; set; }
/// <inheritdoc cref="ServerFieldSet.Domain"/>
public Layout ServerDomain { get; set; }
/// <inheritdoc cref="ServerFieldSet.Ip"/>
public Layout ServerIp { get; set; }
/// <inheritdoc cref="UserFieldSet.Name"/>
Expand Down Expand Up @@ -302,20 +310,17 @@ public NLogEcsDocument RenderEcsDocument(LogEventInfo logEvent)
SetApmTransactionId(ecsEvent, logEvent);
SetApmSpanId(ecsEvent, logEvent);

// prefer setting service information set by Elastic APM
var service = GetService(logEvent);
if (service != null)
ecsEvent.Service = service;
ecsEvent.Agent = GetAgent(logEvent, _defaultAgent);
ecsEvent.Service = GetService(logEvent, ecsEvent.Service);
ecsEvent.Host = GetHost(logEvent, ecsEvent.Host);
ecsEvent.Server = GetServer(logEvent, ecsEvent.Server);

ecsEvent.Message = logEvent.FormattedMessage;
ecsEvent.Log = GetLog(logEvent);
ecsEvent.Event = GetEvent(logEvent);
ecsEvent.Process = GetProcess(logEvent);
ecsEvent.Tags = GetTags(logEvent);
ecsEvent.Labels = GetLabels(logEvent);
ecsEvent.Agent = GetAgent(logEvent) ?? _defaultAgent;
ecsEvent.Server = GetServer(logEvent);
ecsEvent.Host = GetHost(logEvent);
ecsEvent.Http = GetHttp(logEvent);
ecsEvent.Url = GetUrl(logEvent);

Expand All @@ -337,16 +342,6 @@ public NLogEcsDocument RenderEcsDocument(LogEventInfo logEvent)
return ecsEvent;
}

private Service GetService(LogEventInfo logEventInfo)
{
var serviceName = ApmServiceName?.Render(logEventInfo);
if (string.IsNullOrEmpty(serviceName)) return null;

var serviceNodeName = ApmServiceNodeName?.Render(logEventInfo);
var serviceVersion = ApmServiceVersion?.Render(logEventInfo);
return new Service { Name = serviceName, Version = serviceVersion, NodeName = serviceNodeName };
}

/// <summary>
/// Override to supplement the ECS event parsing
/// </summary>
Expand Down Expand Up @@ -547,27 +542,28 @@ private Event GetEvent(LogEventInfo logEventInfo)
return evnt;
}

private Agent GetAgent(LogEventInfo logEventInfo)
private Agent GetAgent(LogEventInfo logEventInfo, Agent defaultAgent)
{
var agentId = AgentId?.Render(logEventInfo);
var agentName = AgentName?.Render(logEventInfo);
var agentType = AgentType?.Render(logEventInfo);
var agentVersion = AgentVersion?.Render(logEventInfo);

if (string.IsNullOrEmpty(agentId)
&& string.IsNullOrEmpty(agentName)
&& string.IsNullOrEmpty(agentType)
&& string.IsNullOrEmpty(agentVersion))
return null;
var previousAgent = _previousAgent ?? defaultAgent;
if ((string.IsNullOrEmpty(agentId) || agentId == previousAgent?.Id)
&& (string.IsNullOrEmpty(agentName) || agentName == previousAgent?.Name)
&& (string.IsNullOrEmpty(agentType) || agentType == previousAgent?.Type)
&& (string.IsNullOrEmpty(agentVersion) || agentVersion == previousAgent?.Version))
return previousAgent;

var agent = new Agent
{
Id = agentId,
Name = agentName,
Type = agentType,
Version = agentVersion
Id = string.IsNullOrEmpty(agentId) ? previousAgent?.Id : agentId,
Name = string.IsNullOrEmpty(agentName) ? previousAgent?.Name : agentName,
Type = string.IsNullOrEmpty(agentType) ? previousAgent?.Type : agentType,
Version = string.IsNullOrEmpty(agentVersion) ? previousAgent?.Version : agentVersion,
};

_previousAgent = agent;
return agent;
}

Expand All @@ -580,41 +576,115 @@ private Process GetProcess(LogEventInfo logEventInfo)
var processThreadId = ProcessThreadId?.Render(logEventInfo);
var processThreadName = ProcessThreadName?.Render(logEventInfo);

if (string.IsNullOrEmpty(processId)
&& string.IsNullOrEmpty(processName)
&& string.IsNullOrEmpty(processTitle)
&& string.IsNullOrEmpty(processExecutable)
&& string.IsNullOrEmpty(processThreadId)
&& string.IsNullOrEmpty(processThreadName))
return null;
if (!string.IsNullOrEmpty(processThreadId) || !string.IsNullOrEmpty(processThreadName))
{
return new Process
{
Title = processTitle,
Name = processName,
Executable = processExecutable,
Pid = !string.IsNullOrEmpty(processId) ? long.Parse(processId) : null,
ThreadId = !string.IsNullOrEmpty(processThreadId) ? long.Parse(processThreadId) : null,
ThreadName = !string.IsNullOrEmpty(processThreadName) ? processThreadName : null,
};
}

// Only attempt to reuse Process-object when not including Thread-details
var previousProcess = _previousProcess;
if ( (string.IsNullOrEmpty(processTitle) || processTitle == previousProcess?.Title)
&& (string.IsNullOrEmpty(processName) || processName == previousProcess?.Name)
&& (string.IsNullOrEmpty(processId) || long.Parse(processId) == previousProcess?.Pid)
&& (string.IsNullOrEmpty(processExecutable) || processExecutable == previousProcess?.Executable))
return previousProcess;

return new Process
var process = new Process
{
Title = processTitle,
Name = processName,
Pid = !string.IsNullOrEmpty(processId) ? long.Parse(processId) : null,
Executable = processExecutable,
ThreadId = !string.IsNullOrEmpty(processThreadId) ? long.Parse(processThreadId) : null,
ThreadName = !string.IsNullOrEmpty(processThreadName) ? processThreadName : null,
Title = string.IsNullOrEmpty(processTitle) ? previousProcess?.Title : processTitle,
Name = string.IsNullOrEmpty(processName) ? previousProcess?.Name : processName,
Executable = string.IsNullOrEmpty(processExecutable) ? previousProcess?.Executable : processExecutable,
Pid = string.IsNullOrEmpty(processId) ? previousProcess?.Pid : long.Parse(processId),
};
_previousProcess = process;
return process;
}

private Server GetServer(LogEventInfo logEventInfo)
private Server GetServer(LogEventInfo logEventInfo, Server defaultServer)
{
var serverUser = ServerUser?.Render(logEventInfo);
var serverAddress = ServerAddress?.Render(logEventInfo);
var serverDomain = ServerDomain?.Render(logEventInfo);
var serverIp = ServerIp?.Render(logEventInfo);
if (string.IsNullOrEmpty(serverUser) && string.IsNullOrEmpty(serverAddress) && string.IsNullOrEmpty(serverIp))
return null;

return new Server
var previousServer = _previousServer ?? defaultServer;
if ((string.IsNullOrEmpty(serverUser) || serverUser == previousServer?.User?.Name)
&& (string.IsNullOrEmpty(serverAddress) || serverAddress == previousServer?.Address)
&& (string.IsNullOrEmpty(serverDomain) || serverDomain == previousServer?.Domain)
&& (string.IsNullOrEmpty(serverIp) || serverIp == previousServer?.Ip))
return previousServer;

var server = new Server
{
User = !string.IsNullOrEmpty(serverUser)
? new User { Name = serverUser }
: null,
Address = serverAddress,
Ip = serverIp
User = string.IsNullOrEmpty(serverUser) ? previousServer?.User : new User() { Name = serverUser },
Address = string.IsNullOrEmpty(serverAddress) ? previousServer?.Address : serverAddress,
Domain = string.IsNullOrEmpty(serverDomain) ? previousServer?.Domain : serverDomain,
Ip = string.IsNullOrEmpty(serverIp) ? previousServer?.Ip : serverIp,
};
_previousServer = server;
return server;
}

private Service GetService(LogEventInfo logEventInfo, Service defaultService)
{
var serviceName = ApmServiceName?.Render(logEventInfo);
if (string.IsNullOrEmpty(serviceName))
return defaultService;

var serviceNodeName = ApmServiceNodeName?.Render(logEventInfo);
var serviceVersion = ApmServiceVersion?.Render(logEventInfo);
var serviceEnvironment = ServiceEnvironment?.Render(logEventInfo);

var previousService = _previousService ?? defaultService;
if ( (string.IsNullOrEmpty(serviceName) || serviceName == previousService?.Name)
&& (string.IsNullOrEmpty(serviceNodeName) || serviceNodeName == previousService?.NodeName)
&& (string.IsNullOrEmpty(serviceVersion) || serviceVersion == previousService?.Version)
&& (string.IsNullOrEmpty(serviceEnvironment) || serviceEnvironment == previousService?.Environment))
return previousService;

var service = new Service
{
Name = string.IsNullOrEmpty(serviceName) ? previousService?.Name : serviceName,
NodeName = string.IsNullOrEmpty(serviceNodeName) ? previousService?.NodeName : serviceNodeName,
Version = string.IsNullOrEmpty(serviceVersion) ? previousService?.Version : serviceVersion,
Environment = string.IsNullOrEmpty(serviceEnvironment) ? previousService?.Environment : serviceEnvironment,
Type = previousService?.Type,
};
_previousService = service;
return service;
}

private Host GetHost(LogEventInfo logEventInfo, Host defaultHost)
{
var hostId = HostId?.Render(logEventInfo);
var hostName = HostName?.Render(logEventInfo);
var hostIp = HostIp?.Render(logEventInfo);

var previousHost = _previousHost ?? defaultHost;
if ((string.IsNullOrEmpty(hostId) || hostId == previousHost?.Id)
&& (string.IsNullOrEmpty(hostName) || hostName == previousHost?.Hostname)
&& (string.IsNullOrEmpty(hostIp) || (previousHost?.Ip?.Length == 1 && hostIp == previousHost.Ip[0])))
return previousHost;

var host = new Host
{
Id = string.IsNullOrEmpty(hostId) ? previousHost?.Id : hostId,
Hostname = string.IsNullOrEmpty(hostName) ? previousHost?.Hostname : hostName,
Ip = string.IsNullOrEmpty(hostIp) ? previousHost?.Ip : new[] { hostIp },
Type = previousHost?.Type,
Architecture = previousHost?.Architecture,
Os = previousHost?.Os,
};
_previousHost = host;
return host;
}

private void SetApmTraceId(EcsDocument ecsDocument, LogEventInfo logEventInfo)
Expand Down Expand Up @@ -690,27 +760,6 @@ private Url GetUrl(LogEventInfo logEventInfo)
return url;
}

private Host GetHost(LogEventInfo logEventInfo)
{
var hostId = HostId?.Render(logEventInfo);
var hostName = HostName?.Render(logEventInfo);
var hostIp = HostIp?.Render(logEventInfo);

if (string.IsNullOrEmpty(hostId)
&& string.IsNullOrEmpty(hostName)
&& string.IsNullOrEmpty(hostIp))
return null;

var host = new Host
{
Id = string.IsNullOrEmpty(hostId) ? null : hostId,
Name = string.IsNullOrEmpty(hostName) ? null : hostName,
Ip = string.IsNullOrEmpty(hostIp) ? null : new[] { hostIp }
};

return host;
}

private static long GetSysLogSeverity(LogLevel logLevel)
{
if (logLevel == LogLevel.Trace || logLevel == LogLevel.Debug)
Expand Down

0 comments on commit 7a51998

Please sign in to comment.