2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.aai.logging;
23 import org.apache.commons.lang.StringUtils;
24 import org.onap.aai.exceptions.AAIException;
25 import org.onap.aai.util.AAIConstants;
26 import org.onap.aai.util.MapperUtil;
27 import org.onap.logging.filter.base.Constants;
28 import org.onap.logging.filter.base.MDCSetup;
29 import org.onap.logging.ref.slf4j.ONAPLogConstants;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
34 import javax.ws.rs.core.MediaType;
35 import javax.xml.bind.JAXBContext;
36 import javax.xml.bind.Marshaller;
37 import java.io.FileInputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.StringWriter;
42 import java.util.Map.Entry;
46 * This classes loads the application error properties file
47 * and provides a method that returns an ErrorObject
51 public class ErrorLogHelper {
52 private static final Logger LOGGER = LoggerFactory.getLogger(ErrorLogHelper.class);
53 private static final HashMap<String, ErrorObject> ERROR_OBJECTS = new HashMap<String, ErrorObject>();
58 } catch (IOException e) {
59 throw new RuntimeException("Failed to load error.properties file", e);
60 } catch (ErrorObjectFormatException e) {
61 throw new RuntimeException("Failed to parse error.properties file", e);
67 * @throws IOException the exception
68 * @throws ErrorObjectFormatException
70 public static void loadProperties() throws IOException, ErrorObjectFormatException {
71 final String filePath = AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "error.properties";
72 final InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("error.properties");
73 final Properties properties = new Properties();
75 try (final FileInputStream fis = new FileInputStream(filePath)) {
76 LOGGER.info("Found the error.properties in the following location: {}", AAIConstants.AAI_HOME_ETC_APP_PROPERTIES);
78 } catch(Exception ex){
79 LOGGER.info("Unable to find the error.properties from filesystem so using file in jar");
83 LOGGER.error("Expected to find the error.properties in the jar but unable to find it");
87 for (Entry<Object, Object> entry : properties.entrySet()) {
88 final String key = (String) entry.getKey();
89 final String value = (String) entry.getValue();
90 final String[] errorProperties = value.split(":");
92 if (errorProperties.length < 7)
93 throw new ErrorObjectFormatException();
95 final ErrorObject errorObject = new ErrorObject();
97 errorObject.setDisposition(errorProperties[0].trim());
98 errorObject.setCategory(errorProperties[1].trim());
99 errorObject.setSeverity(errorProperties[2].trim());
100 errorObject.setErrorCode(errorProperties[3].trim());
101 errorObject.setHTTPResponseCode(errorProperties[4].trim());
102 errorObject.setRESTErrorCode(errorProperties[5].trim());
103 errorObject.setErrorText(errorProperties[6].trim());
104 if (errorProperties.length > 7) {
105 errorObject.setAaiElsErrorCode(errorProperties[7].trim());
108 ERROR_OBJECTS.put(key, errorObject);
113 * Logs a known A&AI exception (i.e. one that can be found in error.properties)
115 * @param code for the error in the error.properties file
116 * @throws IOException
117 * @throws ErrorObjectNotFoundException
119 public static ErrorObject getErrorObject(String code) throws ErrorObjectNotFoundException {
122 throw new IllegalArgumentException("Key cannot be null");
124 final ErrorObject errorObject = ERROR_OBJECTS.get(code);
126 if (errorObject == null) {
127 LOGGER.warn("Unknown AAIException with code=" + code + ". Using default AAIException");
128 return ERROR_OBJECTS.get(AAIException.DEFAULT_EXCEPTION_CODE);
135 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
136 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
137 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
138 * If no error object is embedded in the AAIException, one will be created using the error object from the
141 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
142 * @param variables optional list of variables to flesh out text in error string
143 * @return appropriately formatted JSON response per the REST API spec.
144 * @throws IOException
147 public static String getRESTAPIErrorResponse(AAIException are, ArrayList<String> variables) {
148 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
149 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
151 return getRESTAPIErrorResponse(acceptHeaders, are, variables);
155 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
156 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
157 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
158 * If no error object is embedded in the AAIException, one will be created using the error object from the
161 * @param acceptHeadersOrig the accept headers orig
162 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
163 * @param variables optional list of variables to flesh out text in error string
164 * @return appropriately formatted JSON response per the REST API spec.
166 public static String getRESTAPIErrorResponse(List<MediaType> acceptHeadersOrig, AAIException are,
167 ArrayList<String> variables) {
169 StringBuilder text = new StringBuilder();
170 String response = null;
172 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
173 // we might have an exception but no accept header, so we'll set default to JSON
174 boolean foundValidAcceptHeader = false;
175 for (MediaType mt : acceptHeadersOrig) {
176 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt) || MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) {
177 acceptHeaders.add(mt);
178 foundValidAcceptHeader = true;
181 if (foundValidAcceptHeader == false) {
182 // override the exception, client needs to set an appropriate Accept header
183 are = new AAIException("AAI_4014");
184 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
187 final ErrorObject eo = are.getErrorObject();
189 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
191 ErrorObject restErrorObject;
194 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + restErrorCode);
195 } catch (ErrorObjectNotFoundException e) {
196 LOGGER.warn("Failed to find related error object AAI_" + restErrorCode + " for error object "
197 + eo.getErrorCode() + "; using AAI_" + restErrorCode);
198 restErrorObject = eo;
201 text.append(restErrorObject.getErrorText());
203 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
204 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
205 // error, are ordered based on the error string.
206 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
207 text.append(" (msg=%").append(localDataIndex + 1).append(") (ec=%").append(localDataIndex + 2).append(")");
209 if (variables == null) {
210 variables = new ArrayList<String>();
213 if (variables.size() < localDataIndex) {
214 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
215 while (variables.size() < localDataIndex) {
216 variables.add("null");
220 // This will put the error code and error text into the right positions
221 if (are.getMessage() == null || are.getMessage().length() == 0) {
222 variables.add(localDataIndex++, eo.getErrorText());
224 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
226 variables.add(localDataIndex, eo.getErrorCodeString());
228 for (MediaType mediaType : acceptHeaders) {
229 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType)) {
230 JAXBContext context = null;
232 if (eo.getCategory().equals("1")) {
234 context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class);
235 Marshaller m = context.createMarshaller();
236 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
237 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
239 org.onap.aai.domain.restPolicyException.ObjectFactory factory =
240 new org.onap.aai.domain.restPolicyException.ObjectFactory();
241 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
242 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError =
243 factory.createFaultRequestError();
244 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException =
245 factory.createFaultRequestErrorPolicyException();
246 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables =
247 factory.createFaultRequestErrorPolicyExceptionVariables();
249 policyException.setMessageId("POL" + eo.getRESTErrorCode());
250 policyException.setText(text.toString());
251 for (int i = 0; i < variables.size(); i++) {
252 polvariables.getVariable().add(variables.get(i));
254 policyException.setVariables(polvariables);
255 requestError.setPolicyException(policyException);
256 fault.setRequestError(requestError);
258 StringWriter sw = new StringWriter();
259 m.marshal(fault, sw);
261 response = sw.toString();
265 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
266 Marshaller m = context.createMarshaller();
267 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
268 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
270 org.onap.aai.domain.restServiceException.ObjectFactory factory =
271 new org.onap.aai.domain.restServiceException.ObjectFactory();
272 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
273 org.onap.aai.domain.restServiceException.Fault.RequestError requestError =
274 factory.createFaultRequestError();
275 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException =
276 factory.createFaultRequestErrorServiceException();
277 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables =
278 factory.createFaultRequestErrorServiceExceptionVariables();
279 serviceException.setMessageId("SVC" + eo.getRESTErrorCode());
280 serviceException.setText(text.toString());
281 for (int i = 0; i < variables.size(); i++) {
282 svcvariables.getVariable().add(variables.get(i));
284 serviceException.setVariables(svcvariables);
285 requestError.setServiceException(serviceException);
286 fault.setRequestError(requestError);
288 StringWriter sw = new StringWriter();
289 m.marshal(fault, sw);
291 response = sw.toString();
294 } catch (Exception ex) {
296 "We were unable to create a rest exception to return on an API because of a parsing error "
301 if (eo.getCategory().equals("1")) {
302 org.onap.aai.domain.restPolicyException.RESTResponse restresp =
303 new org.onap.aai.domain.restPolicyException.RESTResponse();
304 org.onap.aai.domain.restPolicyException.RequestError reqerr =
305 new org.onap.aai.domain.restPolicyException.RequestError();
306 org.onap.aai.domain.restPolicyException.PolicyException polexc =
307 new org.onap.aai.domain.restPolicyException.PolicyException();
308 polexc.setMessageId("POL" + eo.getRESTErrorCode());
309 polexc.setText(text.toString());
310 polexc.setVariables(variables);
311 reqerr.setPolicyException(polexc);
312 restresp.setRequestError(reqerr);
313 response = (MapperUtil.writeAsJSONString((Object) restresp));
316 org.onap.aai.domain.restServiceException.RESTResponse restresp =
317 new org.onap.aai.domain.restServiceException.RESTResponse();
318 org.onap.aai.domain.restServiceException.RequestError reqerr =
319 new org.onap.aai.domain.restServiceException.RequestError();
320 org.onap.aai.domain.restServiceException.ServiceException svcexc =
321 new org.onap.aai.domain.restServiceException.ServiceException();
322 svcexc.setMessageId("SVC" + eo.getRESTErrorCode());
323 svcexc.setText(text.toString());
324 svcexc.setVariables(variables);
325 reqerr.setServiceException(svcexc);
326 restresp.setRequestError(reqerr);
327 response = (MapperUtil.writeAsJSONString((Object) restresp));
329 } catch (Exception ex) {
331 "We were unable to create a rest exception to return on an API because of a parsing error "
341 * Gets the RESTAPI error response with logging.
343 * @param acceptHeadersOrig the accept headers orig
345 * @param variables the variables
347 public static String getRESTAPIErrorResponseWithLogging(List<MediaType> acceptHeadersOrig, AAIException are,
348 ArrayList<String> variables) {
349 String response = ErrorLogHelper.getRESTAPIErrorResponse(acceptHeadersOrig, are, variables);
355 * Gets the RESTAPI info response.
357 * @param acceptHeaders the accept headers
358 * @param areList the are list
359 * @return the RESTAPI info response
361 public static Object getRESTAPIInfoResponse(List<MediaType> acceptHeaders,
362 HashMap<AAIException, ArrayList<String>> areList) {
364 Object respObj = null;
366 org.onap.aai.domain.restResponseInfo.ObjectFactory factory =
367 new org.onap.aai.domain.restResponseInfo.ObjectFactory();
368 org.onap.aai.domain.restResponseInfo.Info info = factory.createInfo();
369 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages responseMessages =
370 factory.createInfoResponseMessages();
371 Iterator<Entry<AAIException, ArrayList<String>>> it = areList.entrySet().iterator();
373 while (it.hasNext()) {
374 Entry<AAIException, ArrayList<String>> pair = (Entry<AAIException, ArrayList<String>>) it.next();
375 AAIException are = pair.getKey();
376 ArrayList<String> variables = pair.getValue();
378 StringBuilder text = new StringBuilder();
380 ErrorObject eo = are.getErrorObject();
382 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
383 ErrorObject restErrorObject;
385 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + String.format("%04d", restErrorCode));
386 } catch (ErrorObjectNotFoundException e) {
387 restErrorObject = eo;
389 text.append(restErrorObject.getErrorText());
391 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
392 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
393 // error, are ordered based on the error string.
394 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
395 text.append(" (msg=%").append(localDataIndex + 1).append(") (rc=%").append(localDataIndex + 2).append(")");
397 if (variables == null) {
398 variables = new ArrayList<String>();
401 if (variables.size() < localDataIndex) {
402 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
403 while (variables.size() < localDataIndex) {
404 variables.add("null");
408 // This will put the error code and error text into the right positions
409 if (are.getMessage() == null) {
410 variables.add(localDataIndex++, eo.getErrorText());
412 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
414 variables.add(localDataIndex, eo.getErrorCodeString());
417 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage responseMessage =
418 factory.createInfoResponseMessagesResponseMessage();
419 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage.Variables infovariables =
420 factory.createInfoResponseMessagesResponseMessageVariables();
422 responseMessage.setMessageId("INF" + eo.getRESTErrorCode());
423 responseMessage.setText(text.toString());
424 for (int i = 0; i < variables.size(); i++) {
425 infovariables.getVariable().add(variables.get(i));
428 responseMessage.setVariables(infovariables);
429 responseMessages.getResponseMessage().add(responseMessage);
431 } catch (Exception ex) {
432 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error "
437 info.setResponseMessages(responseMessages);
438 respObj = (Object) info;
444 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
445 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
446 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
447 * If no error object is embedded in the AAIException, one will be created using the error object from the
450 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
451 * @param variables optional list of variables to flesh out text in error string
452 * @return appropriately formatted JSON response per the REST API spec.
454 public static String getRESTAPIPolicyErrorResponseXML(AAIException are, ArrayList<String> variables) {
456 StringBuilder text = new StringBuilder();
457 String response = null;
458 JAXBContext context = null;
460 ErrorObject eo = are.getErrorObject();
462 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
463 ErrorObject restErrorObject;
465 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + restErrorCode);
466 } catch (ErrorObjectNotFoundException e) {
467 restErrorObject = eo;
470 text.append(restErrorObject.getErrorText());
472 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
473 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
474 // error, are ordered based on the error string.
475 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
476 text.append(" (msg=%").append(localDataIndex + 1).append(") (ec=%").append(localDataIndex + 2).append(")");
478 if (variables == null) {
479 variables = new ArrayList<String>();
482 if (variables.size() < localDataIndex) {
483 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
484 while (variables.size() < localDataIndex) {
485 variables.add("null");
489 // This will put the error code and error text into the right positions
490 if (are.getMessage() == null) {
491 variables.add(localDataIndex++, eo.getErrorText());
493 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
495 variables.add(localDataIndex, eo.getErrorCodeString());
498 if (eo.getCategory().equals("1")) {
500 context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class);
501 Marshaller m = context.createMarshaller();
502 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
503 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
505 org.onap.aai.domain.restPolicyException.ObjectFactory factory =
506 new org.onap.aai.domain.restPolicyException.ObjectFactory();
507 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
508 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError =
509 factory.createFaultRequestError();
510 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException =
511 factory.createFaultRequestErrorPolicyException();
512 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables =
513 factory.createFaultRequestErrorPolicyExceptionVariables();
515 policyException.setMessageId("POL" + eo.getRESTErrorCode());
516 policyException.setText(text.toString());
517 for (int i = 0; i < variables.size(); i++) {
518 polvariables.getVariable().add(variables.get(i));
520 policyException.setVariables(polvariables);
521 requestError.setPolicyException(policyException);
522 fault.setRequestError(requestError);
524 StringWriter sw = new StringWriter();
525 m.marshal(fault, sw);
527 response = sw.toString();
531 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
532 Marshaller m = context.createMarshaller();
533 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
534 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
536 org.onap.aai.domain.restServiceException.ObjectFactory factory =
537 new org.onap.aai.domain.restServiceException.ObjectFactory();
538 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
539 org.onap.aai.domain.restServiceException.Fault.RequestError requestError =
540 factory.createFaultRequestError();
541 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException =
542 factory.createFaultRequestErrorServiceException();
543 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables =
544 factory.createFaultRequestErrorServiceExceptionVariables();
545 serviceException.setMessageId("POL" + eo.getRESTErrorCode());
546 serviceException.setText(text.toString());
547 for (int i = 0; i < variables.size(); i++) {
548 svcvariables.getVariable().add(variables.get(i));
550 serviceException.setVariables(svcvariables);
551 requestError.setServiceException(serviceException);
552 fault.setRequestError(requestError);
554 StringWriter sw = new StringWriter();
555 m.marshal(fault, sw);
557 response = sw.toString();
560 } catch (Exception ex) {
561 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error "
567 public static void logException(AAIException e) {
568 final ErrorObject errorObject = e.getErrorObject();
570 String severityCode = errorObject.getSeverityCode(errorObject.getSeverity());
572 Severify should be left empty per Logging Specification 2019.11
573 if (!StringUtils.isEmpty(severityCode)) {
574 int sevCode = Integer.parseInt(severityCode);
575 if (sevCode > 0 && sevCode <= 3) {
576 LoggingContext.severity(sevCode);
580 String stackTrace = "";
582 stackTrace = LogFormatTools.getStackTop(e);
583 } catch (Exception a) {
586 final String errorMessage = new StringBuilder().append(errorObject.getErrorText()).append(":")
587 .append(errorObject.getRESTErrorCode()).append(":").append(errorObject.getHTTPResponseCode())
588 .append(":").append(e.getMessage()).toString().replaceAll("\\n", "^");
590 MDCSetup mdcSetup = new MDCSetup();
591 mdcSetup.setResponseStatusCode(errorObject.getHTTPResponseCode().getStatusCode());
592 mdcSetup.setErrorCode(Integer.parseInt(errorObject.getAaiElsErrorCode()));
593 String serviceName = MDC.get(ONAPLogConstants.MDCs.SERVICE_NAME);
594 if (serviceName == null || serviceName.isEmpty()) {
595 MDC.put(ONAPLogConstants.MDCs.SERVICE_NAME, Constants.DefaultValues.UNKNOWN);
597 MDC.put(ONAPLogConstants.MDCs.ERROR_DESC, errorMessage);
598 final String details =
599 new StringBuilder().append(errorObject.getErrorCodeString()).append(" ").append(stackTrace).toString();
601 if (errorObject.getSeverity().equalsIgnoreCase("WARN"))
602 LOGGER.warn(details);
603 else if (errorObject.getSeverity().equalsIgnoreCase("ERROR"))
604 LOGGER.error(details);
605 else if (errorObject.getSeverity().equalsIgnoreCase("FATAL"))
606 LOGGER.error(details);
607 else if (errorObject.getSeverity().equals("INFO"))
608 LOGGER.info(details);
611 public static void logError(String code) {
615 public static void logError(String code, String message) {
616 logException(new AAIException(code, message));