Skip to content

Commit 6db99ad

Browse files
exceptionfactorypvillard31
authored andcommitted
NIFI-14991 Added Process Group Attributes to Component Log
- Added Process Group properties as attributes to Standard Logging Context - Updated standard Component Log implementation to include Process Group attributes as MDC attributes - Added MDC section to Admin Guide Signed-off-by: Pierre Villard <[email protected]> This closes #10330.
1 parent ae5df39 commit 6db99ad

File tree

9 files changed

+356
-16
lines changed

9 files changed

+356
-16
lines changed

nifi-docs/src/main/asciidoc/administration-guide.adoc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,29 @@ The standard logback configuration includes the following appender definitions a
197197
| `nifi-user.log` | User log containing authentication and authorization messages
198198
|=========================
199199

200+
=== Mapped Diagnostic Context Properties
201+
Logging for extension components such as Processors and Controller Services include variables in the
202+
link:https://www.slf4j.org/api/org/slf4j/MDC.html[Mapped Diagnostic Context] that provide additional information about
203+
the component location. link:https://logback.qos.ch/manual/layouts.html#mdc[MDC] values can be added to log messages
204+
with custom layout configuration.
205+
206+
Component logs provide the following MDC named values:
207+
208+
- `processGroupId` contains the UUID of the Process Group
209+
- `processGroupIdPath` contains the hierarchy of UUIDs for Process Groups with separators
210+
- `processGroupName` contains the name of the Process Group
211+
- `processGroupNamePath` contains of the hierarchy of names for Process Groups with separators
212+
213+
MDC named values can be added to a Logback pattern layout using the `mdc` conversion word.
214+
215+
[source, xml]
216+
----
217+
<pattern>%date %level [%thread] %mdc{processGroupId} %logger{40} %msg%n</pattern>
218+
----
219+
220+
Logs from classes other than extension components do not have MDC named values. Logs formatted using the pattern layout
221+
will include an empty space when an MDC named value is not found.
222+
200223
=== Deprecation Logging
201224
The `nifi-deprecation.log` contains warning messages describing components and features that will be removed in
202225
subsequent versions. Deprecation warnings should be evaluated and addressed to avoid breaking changes when upgrading to

nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ public final class StandardProcessGroup implements ProcessGroup {
214214
private static final long DEFAULT_BACKPRESSURE_OBJECT = 10_000L;
215215
private static final String DEFAULT_BACKPRESSURE_DATA_SIZE = "1 GB";
216216
private static final Pattern INVALID_DIRECTORY_NAME_CHARACTERS = Pattern.compile("[\\s\\<\\>:\\'\\\"\\/\\\\\\|\\?\\*]");
217+
private static final String PATH_SEPARATOR = "/";
218+
private static final String STANDARD_PROCESS_GROUP_NAME = "StandardProcessGroup";
219+
220+
private final Map<String, String> loggingAttributes = new ConcurrentHashMap<>();
217221
private volatile String logFileSuffix;
218222

219223

@@ -286,6 +290,7 @@ public ProcessGroup getParent() {
286290
@Override
287291
public void setParent(final ProcessGroup newParent) {
288292
parent.set(newParent);
293+
setLoggingAttributes();
289294
}
290295

291296
@Override
@@ -325,6 +330,7 @@ public void setName(final String name) {
325330
}
326331

327332
this.name.set(name);
333+
setLoggingAttributes();
328334
}
329335

330336
@Override
@@ -4352,6 +4358,16 @@ public QueueSize getQueueSize() {
43524358
return new QueueSize(count, contentSize);
43534359
}
43544360

4361+
/**
4362+
* Get Map of Attribute Names and Values to provide additional context for logging
4363+
*
4364+
* @return Map of Attribute Names and Values
4365+
*/
4366+
@Override
4367+
public Map<String, String> getLoggingAttributes() {
4368+
return Collections.unmodifiableMap(loggingAttributes);
4369+
}
4370+
43554371
@Override
43564372
public String getLogFileSuffix() {
43574373
return logFileSuffix;
@@ -4542,4 +4558,63 @@ public void setStatelessFlowTimeout(final String statelessFlowTimeout) {
45424558
LOG.warn("Attempted to set Stateless Flow Timeout for {} to invalid value: {}; ignoring this value", this, statelessFlowTimeout);
45434559
}
45444560
}
4561+
4562+
private void setLoggingAttributes() {
4563+
loggingAttributes.put(LoggingAttribute.PROCESS_GROUP_ID.attribute, id);
4564+
4565+
final String processGroupName = name.get();
4566+
if (processGroupName == null) {
4567+
loggingAttributes.put(LoggingAttribute.PROCESS_GROUP_NAME.attribute, STANDARD_PROCESS_GROUP_NAME);
4568+
} else {
4569+
loggingAttributes.put(LoggingAttribute.PROCESS_GROUP_NAME.attribute, processGroupName);
4570+
setGroupPath();
4571+
}
4572+
}
4573+
4574+
private void setGroupPath() {
4575+
final StringBuilder namePathBuilder = new StringBuilder();
4576+
namePathBuilder.append(PATH_SEPARATOR);
4577+
namePathBuilder.append(name.get());
4578+
4579+
final StringBuilder idPathBuilder = new StringBuilder();
4580+
idPathBuilder.append(PATH_SEPARATOR);
4581+
idPathBuilder.append(id);
4582+
4583+
ProcessGroup parentProcessGroup = getParent();
4584+
while (parentProcessGroup != null) {
4585+
namePathBuilder.insert(0, PATH_SEPARATOR);
4586+
namePathBuilder.insert(1, parentProcessGroup.getName());
4587+
4588+
idPathBuilder.insert(0, PATH_SEPARATOR);
4589+
idPathBuilder.insert(1, parentProcessGroup.getIdentifier());
4590+
4591+
parentProcessGroup = parentProcessGroup.getParent();
4592+
}
4593+
4594+
final String idPath = idPathBuilder.toString();
4595+
loggingAttributes.put(LoggingAttribute.PROCESS_GROUP_ID_PATH.attribute, idPath);
4596+
4597+
final String namePath = namePathBuilder.toString();
4598+
loggingAttributes.put(LoggingAttribute.PROCESS_GROUP_NAME_PATH.attribute, namePath);
4599+
}
4600+
4601+
enum LoggingAttribute {
4602+
PROCESS_GROUP_ID("processGroupId"),
4603+
4604+
PROCESS_GROUP_ID_PATH("processGroupIdPath"),
4605+
4606+
PROCESS_GROUP_NAME("processGroupName"),
4607+
4608+
PROCESS_GROUP_NAME_PATH("processGroupNamePath");
4609+
4610+
private final String attribute;
4611+
4612+
LoggingAttribute(final String attribute) {
4613+
this.attribute = attribute;
4614+
}
4615+
4616+
String getAttribute() {
4617+
return attribute;
4618+
}
4619+
}
45454620
}

nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/logging/LoggingContext.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,17 @@
1616
*/
1717
package org.apache.nifi.logging;
1818

19+
import java.util.Map;
1920
import java.util.Optional;
2021

2122
public interface LoggingContext {
23+
/**
24+
* Get Attributes
25+
*
26+
* @return Map of Attribute Name to Value such as Process Group information when provided
27+
*/
28+
Map<String, String> getAttributes();
29+
2230
/**
2331
* @return the log file name suffix. This will be the discriminating value for the dedicated logging.
2432
*/

nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/logging/StandardLoggingContext.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,41 @@
1818

1919
import org.apache.nifi.groups.ProcessGroup;
2020

21+
import java.util.Map;
2122
import java.util.Optional;
2223

23-
24-
2524
public class StandardLoggingContext implements LoggingContext {
2625
private static final String KEY = "logFileSuffix";
26+
2727
private volatile GroupedComponent component;
2828

2929
public StandardLoggingContext(final GroupedComponent component) {
3030
this.component = component;
3131
}
3232

33+
/**
34+
* Get Attributes from Component Process Group
35+
*
36+
* @return Attributes from Process Group or empty when Component or Process Group not found
37+
*/
38+
@Override
39+
public Map<String, String> getAttributes() {
40+
final Map<String, String> attributes;
41+
42+
if (component == null) {
43+
attributes = Map.of();
44+
} else {
45+
final ProcessGroup processGroup = component.getProcessGroup();
46+
if (processGroup == null) {
47+
attributes = Map.of();
48+
} else {
49+
attributes = processGroup.getLoggingAttributes();
50+
}
51+
}
52+
53+
return attributes;
54+
}
55+
3356
@Override
3457
public Optional<String> getLogFileSuffix() {
3558
if (component != null) {

nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@
2525
import org.apache.nifi.logging.LoggingContext;
2626
import org.slf4j.Logger;
2727
import org.slf4j.LoggerFactory;
28+
import org.slf4j.MDC;
2829
import org.slf4j.event.Level;
30+
import org.slf4j.spi.LoggingEventBuilder;
2931

3032
import java.util.ArrayList;
3133
import java.util.List;
34+
import java.util.Map;
35+
import java.util.Optional;
36+
import java.util.Set;
3237

3338
public class SimpleProcessLogger implements ComponentLog {
3439

@@ -458,17 +463,24 @@ private Throwable findLastThrowable(final Object[] arguments) {
458463
return lastThrowable;
459464
}
460465

461-
private String getDiscriminatorKey() {
462-
return loggingContext.getDiscriminatorKey();
463-
}
466+
private void log(final Level level, final String message, final Object... arguments) {
467+
LoggingEventBuilder builder = logger.makeLoggingEventBuilder(level);
464468

465-
private String getLogFileSuffix() {
466-
return loggingContext.getLogFileSuffix().orElse(null);
467-
}
469+
final Optional<String> loggingFileSuffixFound = loggingContext.getLogFileSuffix();
470+
if (loggingFileSuffixFound.isPresent()) {
471+
final String logFileSuffix = loggingFileSuffixFound.get();
472+
builder = builder.addKeyValue(loggingContext.getDiscriminatorKey(), logFileSuffix);
473+
}
468474

469-
private void log(final Level level, final String message, final Object... arguments) {
470-
logger.makeLoggingEventBuilder(level)
471-
.addKeyValue(getDiscriminatorKey(), getLogFileSuffix())
472-
.log(message, arguments);
475+
// Add attributes to Mapped Diagnostic Context for logging
476+
final Map<String, String> attributes = loggingContext.getAttributes();
477+
final Set<String> attributeKeys = attributes.keySet();
478+
try {
479+
attributes.forEach(MDC::put);
480+
builder.log(message, arguments);
481+
} finally {
482+
// Remove MDC attributes after logging
483+
attributeKeys.forEach(MDC::remove);
484+
}
473485
}
474486
}

0 commit comments

Comments
 (0)