1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.audit;
18
19 import org.apache.logging.log4j.LogManager;
20 import org.apache.logging.log4j.Logger;
21 import org.apache.logging.log4j.ThreadContext;
22 import org.apache.logging.log4j.audit.catalog.CatalogManager;
23 import org.apache.logging.log4j.audit.exception.AuditException;
24 import org.apache.logging.log4j.audit.util.NamingUtils;
25 import org.apache.logging.log4j.catalog.api.Attribute;
26 import org.apache.logging.log4j.catalog.api.Constraint;
27 import org.apache.logging.log4j.catalog.api.Event;
28 import org.apache.logging.log4j.catalog.api.EventAttribute;
29 import org.apache.logging.log4j.audit.exception.ConstraintValidationException;
30 import org.apache.logging.log4j.catalog.api.plugins.ConstraintPlugins;
31 import org.apache.logging.log4j.message.StructuredDataMessage;
32
33 import java.util.*;
34
35 import static java.util.Collections.*;
36
37
38
39
40 public abstract class AbstractEventLogger {
41
42 private static final Logger logger = LogManager.getLogger(AbstractEventLogger.class);
43
44 private static final int DEFAULT_MAX_LENGTH = 32;
45
46 private static ConstraintPlugins constraintPlugins = ConstraintPlugins.getInstance();
47
48 public CatalogManager catalogManager;
49
50 private static final AuditExceptionHandler DEFAULT_EXCEPTION_HANDLER = (message, ex) -> {
51 throw new AuditException("Error logging event " + message.getId().getName(), ex);
52 };
53
54 private static final AuditExceptionHandler NOOP_EXCEPTION_HANDLER = (message, ex) -> {
55 };
56
57 private AuditExceptionHandler defaultAuditExceptionHandler = DEFAULT_EXCEPTION_HANDLER;
58
59 private final int maxLength;
60
61 protected AbstractEventLogger() {
62 maxLength = DEFAULT_MAX_LENGTH;
63 }
64
65 protected AbstractEventLogger(int maxLength) {
66 this.maxLength = maxLength;
67 }
68
69 public void setCatalogManager(CatalogManager catalogManager) {
70 this.catalogManager = catalogManager;
71 }
72
73 public List<String> getAttributeNames(String eventId) {
74 return catalogManager.getAttributeNames(eventId);
75 }
76
77 public void setDefaultAuditExceptionHandler(AuditExceptionHandler auditExceptionHandler) {
78 defaultAuditExceptionHandler = auditExceptionHandler == null ? NOOP_EXCEPTION_HANDLER : auditExceptionHandler;
79 }
80
81 public void logEvent(String eventName, Map<String, String> attributes) {
82 logEvent(eventName, null, attributes, defaultAuditExceptionHandler);
83 }
84
85 public void logEvent(String eventName, String catalogId, Map<String, String> attributes) {
86 logEvent(eventName, catalogId, attributes, defaultAuditExceptionHandler);
87 }
88
89 public void logEvent(String eventName, Map<String, String> attributes, AuditExceptionHandler exceptionHandler) {
90 logEvent(eventName, null, attributes, exceptionHandler);
91 }
92
93 private void logEvent(String eventName, String catalogId, Map<String, String> attributes, AuditExceptionHandler exceptionHandler) {
94 String eventId = NamingUtils.lowerFirst(eventName);
95 Event event = catalogId == null ? catalogManager.getEvent(eventId) : catalogManager.getEvent(eventId, catalogId);
96 if (event == null) {
97 throw new AuditException("Unable to locate definition of audit event " + eventId);
98 }
99 logEvent(eventId, attributes, event, exceptionHandler);
100 }
101
102 protected abstract void logEvent(StructuredDataMessage message);
103
104 private void logEvent(String eventName, Map<String, String> attributes, Event event,
105 AuditExceptionHandler exceptionHandler) {
106 AuditMessage msg = new AuditMessage(eventName, maxLength);
107
108 if (attributes == null) {
109 attributes = emptyMap();
110 }
111
112 StringBuilder missingAttributes = new StringBuilder();
113 StringBuilder errors = new StringBuilder();
114
115 List<EventAttribute> eventAttributes = event.getAttributes() == null ? emptyList() : event.getAttributes();
116 for (EventAttribute eventAttribute : eventAttributes) {
117 Attribute attr = catalogManager.getAttribute(eventAttribute.getName(), event.getCatalogId());
118 if ((!attr.isRequestContext() && (attr.isRequired()) ||
119 (eventAttribute.isRequired() != null && eventAttribute.isRequired()))) {
120 String name = attr.getName();
121 if (!attributes.containsKey(name)) {
122 if (missingAttributes.length() > 0) {
123 missingAttributes.append(", ");
124 }
125 missingAttributes.append(name);
126 } else {
127 if (attr.getConstraints() != null && attr.getConstraints().size() > 0) {
128 validateConstraints(false, attr.getConstraints(), name, attributes.get(name), errors);
129 }
130 }
131 }
132 }
133 Map<String, Attribute> attributeMap = catalogManager.getAttributes(eventName, event.getCatalogId());
134 for (String name : attributes.keySet()) {
135 if (!attributeMap.containsKey(name) && !name.equals("completionStatus")) {
136 if (errors.length() > 0) {
137 errors.append("\n");
138 }
139 errors.append("Attribute ").append(name).append(" is not defined for ").append(eventName);
140 }
141 }
142 if (missingAttributes.length() > 0) {
143 if (errors.length() > 0) {
144 errors.append("\n");
145 }
146 errors.append("Event ").append(eventName).append(" is missing required attribute(s) ").append(missingAttributes.toString());
147 }
148 if (errors.length() > 0) {
149 throw new ConstraintValidationException(errors.toString());
150 }
151 List<String> attributeNames = catalogManager.getAttributeNames(eventName, event.getCatalogId());
152 StringBuilder buf = new StringBuilder();
153 for (String attribute : attributes.keySet()) {
154 if (!attributeNames.contains(attribute)) {
155 if (buf.length() > 0) {
156 buf.append(", ");
157 }
158 buf.append(attribute);
159 }
160 }
161 if (buf.length() > 0) {
162 throw new ConstraintValidationException("Event " + eventName + " contains invalid attribute(s) " + buf.toString());
163 }
164
165 List<String> reqCtxAttrs = catalogManager.getRequiredContextAttributes(eventName, event.getCatalogId());
166 if (reqCtxAttrs != null && !reqCtxAttrs.isEmpty()) {
167 StringBuilder sb = new StringBuilder();
168 for (String attr : reqCtxAttrs) {
169 if (!ThreadContext.containsKey(attr)) {
170 if (sb.length() > 0) {
171 sb.append(", ");
172 }
173 sb.append(attr);
174 }
175 }
176 if (sb.length() > 0) {
177 throw new ConstraintValidationException("Event " + msg.getId().getName() +
178 " is missing required RequestContextMapping values for " + sb.toString());
179 }
180 }
181
182 Map<String, Attribute> reqCtxAttributes = catalogManager.getRequestContextAttributes();
183 for (Map.Entry<String, Attribute> entry : reqCtxAttributes.entrySet()) {
184 Attribute attribute = entry.getValue();
185 String attr = entry.getKey();
186 if (attribute.isRequired() && !ThreadContext.containsKey(attr)) {
187 if (errors.length() > 0) {
188 errors.append(", ");
189 }
190 errors.append(attr);
191 }
192 }
193 if (errors.length() > 0) {
194 throw new ConstraintValidationException("Event " + eventName +
195 " is missing required Thread Context values for " + errors.toString());
196 }
197
198 for (Map.Entry<String, Attribute> entry : reqCtxAttributes.entrySet()) {
199 Attribute attribute = reqCtxAttributes.get(entry.getKey());
200 if (!ThreadContext.containsKey(entry.getKey())) {
201 continue;
202 }
203 Set<Constraint> constraintList = attribute.getConstraints();
204 if (constraintList != null && constraintList.size() > 0) {
205 validateConstraints(true, constraintList, entry.getKey(), ThreadContext.get(entry.getKey()), errors);
206 }
207 }
208 if (errors.length() > 0) {
209 throw new ConstraintValidationException("Event " + eventName + " has incorrect data in the Thread Context: " + errors.toString());
210 }
211
212 msg.putAll(attributes);
213 try {
214 logEvent(msg);
215 } catch (Throwable ex) {
216 if (exceptionHandler == null) {
217 defaultAuditExceptionHandler.handleException(msg, ex);
218 } else {
219 exceptionHandler.handleException(msg, ex);
220 }
221 }
222 }
223
224 private static void validateConstraints(boolean isRequestContext, Collection<Constraint> constraints, String name,
225 String value, StringBuilder errors) {
226 for (Constraint constraint : constraints) {
227 constraintPlugins.validateConstraint(isRequestContext, constraint.getConstraintType().getName(), name, value,
228 constraint.getValue(), errors);
229 }
230 }
231 }