diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/AgentLinkingMetadata.java b/newrelic-agent/src/main/java/com/newrelic/agent/AgentLinkingMetadata.java index 8754a8fd1e..7e40d340b8 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/AgentLinkingMetadata.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/AgentLinkingMetadata.java @@ -90,7 +90,7 @@ public static Map getLogEventLinkingMetadata(TraceMetadata trace logEventLinkingMetadata.put(HOSTNAME, hostname); } - String entityName = getEntityName(agentConfig); + String entityName = rpmService.getApplicationName(); if (!entityName.isEmpty()) { logEventLinkingMetadata.put(ENTITY_NAME, entityName); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingConfig.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingConfig.java index caad4012ca..92bee041a9 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingConfig.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingConfig.java @@ -93,4 +93,11 @@ public interface ApplicationLoggingConfig { * @return a Set of excluded labels */ Set getLogLabelsExcludeSet(); + + /** + * Return the enabled flag for the auto_app_naming_association flag + * + * @return true if auto_app_naming_association is enabled + */ + boolean isAutoAppNamingAssociationEnabled(); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingConfigImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingConfigImpl.java index 7d12080e8b..1f4d5daa76 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingConfigImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingConfigImpl.java @@ -140,5 +140,10 @@ public Map removeExcludedLogLabels(Map labels) { public Set getLogLabelsExcludeSet() { return applicationLoggingForwardingConfig.getLoggingLabelsExcludeSet(); } + + @Override + public boolean isAutoAppNamingAssociationEnabled() { + return applicationLoggingForwardingConfig.isAutoAppNamingAssociationEnabled(); + } } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingForwardingConfig.java b/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingForwardingConfig.java index 67757cd598..79803cf48d 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingForwardingConfig.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/config/ApplicationLoggingForwardingConfig.java @@ -8,6 +8,7 @@ package com.newrelic.agent.config; import com.newrelic.agent.Agent; +import com.newrelic.api.agent.NewRelic; import java.util.Collections; import java.util.List; @@ -19,12 +20,16 @@ public class ApplicationLoggingForwardingConfig extends BaseConfig { public static final String ROOT = "forwarding"; public static final String ENABLED = "enabled"; public static final String MAX_SAMPLES_STORED = "max_samples_stored"; + public static final String AUTO_APP_NAMING_ASSOC = "auto_app_naming_association"; public static final boolean DEFAULT_ENABLED = true; public static final int DEFAULT_MAX_SAMPLES_STORED = 10000; + public static final boolean DEFAULT_AUTO_APP_NAMING_ASSOC = false; private final boolean enabled; private final int maxSamplesStored; + private final boolean autoAppNamingAssociation; + private final ApplicationLoggingContextDataConfig contextDataConfig; private final ApplicationLoggingLabelsConfig loggingLabelsConfig; @@ -35,6 +40,7 @@ public ApplicationLoggingForwardingConfig(Map props, String pare enabled = storedMoreThan0 && !highSecurity && getProperty(ENABLED, DEFAULT_ENABLED); contextDataConfig = createContextDataConfig(highSecurity); loggingLabelsConfig = createLoggingLabelsConfig(); + autoAppNamingAssociation = initAutoAppNamingAssociationEnabledFlag(); } private int initMaxSamplesStored() { @@ -57,6 +63,15 @@ private ApplicationLoggingLabelsConfig createLoggingLabelsConfig() { return new ApplicationLoggingLabelsConfig(labelsProps, systemPropertyPrefix); } + private boolean initAutoAppNamingAssociationEnabledFlag() { + // If enable_auto_app_naming is false, this association flag must also be false + if (!NewRelic.getAgent().getConfig().getValue(AgentConfigImpl.ENABLE_AUTO_APP_NAMING, false)) { + return false; + } else { + return getProperty(AUTO_APP_NAMING_ASSOC, DEFAULT_AUTO_APP_NAMING_ASSOC); + } + } + public boolean getEnabled() { return enabled; } @@ -81,6 +96,10 @@ public boolean isLogLabelsEnabled() { return enabled && loggingLabelsConfig.getEnabled(); } + public boolean isAutoAppNamingAssociationEnabled() { + return autoAppNamingAssociation; + } + public Map removeExcludedLabels(Map labels) { return loggingLabelsConfig.removeExcludedLabels(labels); } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/service/logging/LogSenderServiceImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/service/logging/LogSenderServiceImpl.java index 0b2dde876d..2cb117c711 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/service/logging/LogSenderServiceImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/service/logging/LogSenderServiceImpl.java @@ -14,6 +14,7 @@ import com.newrelic.agent.AgentLinkingMetadata; import com.newrelic.agent.ExtendedTransactionListener; import com.newrelic.agent.Harvestable; +import com.newrelic.agent.IRPMService; import com.newrelic.agent.MetricNames; import com.newrelic.agent.TraceMetadataImpl; import com.newrelic.agent.Transaction; @@ -74,6 +75,7 @@ public class LogSenderServiceImpl extends AbstractService implements LogSenderSe // to properly calculate the per harvest cycle maxSamplesStored // we'll default to 5000, unless overridden volatile long reportPeriodInMillis = 5000; + private volatile boolean autoAppNamingAssociationEnabled; // Key is app name, value is collection of per-transaction log events for next harvest for that app. private final ConcurrentHashMap> reservoirForApp = new ConcurrentHashMap<>(); @@ -123,6 +125,7 @@ public void configChanged(String appName, AgentConfig agentConfig) { maxSamplesStored = (int) (appLoggingConfig.getMaxSamplesStored()*(reportPeriodInMillis / 60000.0)); forwardingEnabled = appLoggingConfig.isForwardingEnabled(); contextDataKeyFilter = createContextDataKeyFilter(appLoggingConfig); + autoAppNamingAssociationEnabled = appLoggingConfig.isAutoAppNamingAssociationEnabled(); boolean metricsEnabled = appLoggingConfig.isMetricsEnabled(); boolean localDecoratingEnabled = appLoggingConfig.isLocalDecoratingEnabled(); @@ -167,6 +170,7 @@ public LogSenderServiceImpl() { maxSamplesStored = (int) (appLoggingConfig.getMaxSamplesStored()*(reportPeriodInMillis / 60000.0)); forwardingEnabled = appLoggingConfig.isForwardingEnabled(); + autoAppNamingAssociationEnabled = appLoggingConfig.isAutoAppNamingAssociationEnabled(); contextDataKeyFilter = createContextDataKeyFilter(appLoggingConfig); logLabelsEnabled = appLoggingConfig.isLogLabelsEnabled(); labels = appLoggingConfig.removeExcludedLogLabels(config.getLabelsConfig().getLabels()); @@ -361,7 +365,7 @@ private void createAndStoreEvent(String appName, Map attribu return; } DistributedSamplingPriorityQueue eventList = getReservoir(appName); - eventList.add(createValidatedEvent(attributes, contextDataKeyFilter)); + eventList.add(createValidatedEvent(attributes, contextDataKeyFilter, autoAppNamingAssociationEnabled)); Agent.LOG.finest(MessageFormat.format("Added event of type {0}", LOG_EVENT_TYPE)); } @@ -524,11 +528,20 @@ private static String mapInternString(String value) { * * @param attributes Map of attributes to create a LogEvent from * @param contextDataKeyFilter + * @param autoAppNamingAssociationEnabled true if enabled_auto_app_naming and auto_app_naming_association + * is also true. If true, logs will be associated with their corresponding entity and not the roll up + * entity. + * * @return LogEvent instance */ - private static LogEvent createValidatedEvent(Map attributes, ExcludeIncludeFilter contextDataKeyFilter) { + private static LogEvent createValidatedEvent(Map attributes, ExcludeIncludeFilter contextDataKeyFilter, + boolean autoAppNamingAssociationEnabled) { + // Insure we get the correct RPMService instance if auto app naming is enabled + Transaction txn = ServiceFactory.getTransactionService().getTransaction(false); + IRPMService rpmService = (txn == null || !autoAppNamingAssociationEnabled) ? ServiceFactory.getRPMService() : ServiceFactory.getRPMServiceManager().getOrCreateRPMService(txn.getApplicationName()); + Map logEventLinkingMetadata = AgentLinkingMetadata.getLogEventLinkingMetadata(TraceMetadataImpl.INSTANCE, - ServiceFactory.getConfigService(), ServiceFactory.getRPMService()); + ServiceFactory.getConfigService(), rpmService); // Initialize new logEventAttributes map with agent linking metadata Map logEventAttributes = new HashMap<>(logEventLinkingMetadata); @@ -621,11 +634,13 @@ public Logs getTransactionLogs(AgentConfig config) { public static final class TransactionLogs implements Logs { private final Queue events; private final ExcludeIncludeFilter contextDataKeyFilter; + private final boolean autoAppNamingAssociationEnabled; TransactionLogs(AgentConfig config, ExcludeIncludeFilter contextDataKeyFilter) { int maxSamplesStored = config.getApplicationLoggingConfig().getMaxSamplesStored(); events = maxSamplesStored == 0 ? NoOpQueue.getInstance() : new LinkedBlockingQueue<>(maxSamplesStored); this.contextDataKeyFilter = contextDataKeyFilter; + autoAppNamingAssociationEnabled = config.getApplicationLoggingConfig().isAutoAppNamingAssociationEnabled(); } @Override @@ -635,7 +650,7 @@ public void recordLogEvent(Map attributes) { return; } - LogEvent event = createValidatedEvent(attributes, contextDataKeyFilter); + LogEvent event = createValidatedEvent(attributes, contextDataKeyFilter, autoAppNamingAssociationEnabled); if (events.offer(event)) { Agent.LOG.log(Level.FINEST, "Added event of type {0} in Transaction.", LOG_EVENT_TYPE); } else { diff --git a/newrelic-agent/src/main/resources/newrelic.yml b/newrelic-agent/src/main/resources/newrelic.yml index a68f94aab4..00b802d519 100644 --- a/newrelic-agent/src/main/resources/newrelic.yml +++ b/newrelic-agent/src/main/resources/newrelic.yml @@ -114,6 +114,11 @@ common: &default_settings # When true, application logs will be forwarded to New Relic. The default is true. enabled: true + # When true and "enable_auto_app_naming" is also true, log events will be sent + # to their corresponding named entity rather than the roll up entity. + # Default is false. + auto_app_naming_association: false + # Application log events are collected up to the configured amount. Afterwards, # events are sampled to maintain an even distribution across the harvest cycle. # Default is 10000. Setting to 0 will disable. diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/AgentLinkingMetadataTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/AgentLinkingMetadataTest.java index adce108d9c..e3983d79f3 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/AgentLinkingMetadataTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/AgentLinkingMetadataTest.java @@ -87,6 +87,7 @@ public void getLinkingMetadataWithEmptyTraceAttributes() { when(configServiceMock.getDefaultAgentConfig()).thenReturn(agentConfigMock); when(agentConfigMock.getApplicationName()).thenReturn(expectedEntityName); when(rpmServiceMock.getEntityGuid()).thenReturn(expectedEntityGuid); + when(rpmServiceMock.getApplicationName()).thenReturn(expectedEntityName); // Then Map linkingMetadata = AgentLinkingMetadata.getLinkingMetadata(traceMetadataMock, ServiceFactory.getConfigService(), @@ -133,6 +134,7 @@ public void getLogEventLinkingMetadata() { when(configServiceMock.getDefaultAgentConfig()).thenReturn(agentConfigMock); when(agentConfigMock.getApplicationName()).thenReturn(expectedEntityName); when(rpmServiceMock.getEntityGuid()).thenReturn(expectedEntityGuid); + when(rpmServiceMock.getApplicationName()).thenReturn(expectedEntityName); // Then Map linkingMetadata = AgentLinkingMetadata.getLogEventLinkingMetadata(traceMetadataMock, ServiceFactory.getConfigService(), @@ -177,6 +179,7 @@ public void getLogEventLinkingMetadataWithEmptyTraceAttributes() { when(configServiceMock.getDefaultAgentConfig()).thenReturn(agentConfigMock); when(agentConfigMock.getApplicationName()).thenReturn(expectedEntityName); when(rpmServiceMock.getEntityGuid()).thenReturn(expectedEntityGuid); + when(rpmServiceMock.getApplicationName()).thenReturn(expectedEntityName); // Then Map linkingMetadata = AgentLinkingMetadata.getLogEventLinkingMetadata(traceMetadataMock, ServiceFactory.getConfigService(), diff --git a/newrelic-agent/src/test/java/com/newrelic/agent/config/ApplicationLoggingForwardingConfigTest.java b/newrelic-agent/src/test/java/com/newrelic/agent/config/ApplicationLoggingForwardingConfigTest.java index 18d0fa27e1..f44368f6bc 100644 --- a/newrelic-agent/src/test/java/com/newrelic/agent/config/ApplicationLoggingForwardingConfigTest.java +++ b/newrelic-agent/src/test/java/com/newrelic/agent/config/ApplicationLoggingForwardingConfigTest.java @@ -31,6 +31,7 @@ public void defaultForwardingConfig() { ApplicationLoggingForwardingConfig config = new ApplicationLoggingForwardingConfig(localProps, ApplicationLoggingConfigImpl.SYSTEM_PROPERTY_ROOT, false); assertTrue(config.getEnabled()); + assertFalse(config.isAutoAppNamingAssociationEnabled()); } @Test