All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datarouter.exception.service.DefaultExceptionRecorder Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2009 HotPads ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.datarouter.exception.service;

import java.util.List;
import java.util.Optional;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.datarouter.auth.session.CurrentSessionInfo;
import io.datarouter.auth.session.Session;
import io.datarouter.exception.config.DatarouterExceptionSettingRoot;
import io.datarouter.exception.conveyors.DatarouterExceptionBuffers;
import io.datarouter.exception.storage.exceptionrecord.ExceptionRecord;
import io.datarouter.exception.storage.exceptionrecord.ExceptionRecordKey;
import io.datarouter.exception.storage.httprecord.HttpRequestRecord;
import io.datarouter.exception.utils.ExceptionDetailsDetector;
import io.datarouter.exception.utils.ExceptionDetailsDetector.ExceptionRecorderDetails;
import io.datarouter.instrumentation.exception.ExceptionRecordDto;
import io.datarouter.instrumentation.validation.DatarouterInstrumentationValidationConstants.ExceptionInstrumentationConstants;
import io.datarouter.storage.config.properties.EnvironmentName;
import io.datarouter.storage.config.properties.ServerName;
import io.datarouter.storage.config.properties.ServiceName;
import io.datarouter.storage.exception.ExceptionCategory;
import io.datarouter.storage.exception.UnknownExceptionCategory;
import io.datarouter.web.app.WebappName;
import io.datarouter.web.config.DatarouterWebSettingRoot;
import io.datarouter.web.dispatcher.Dispatcher;
import io.datarouter.web.exception.ExceptionCounters;
import io.datarouter.web.exception.ExceptionRecorder;
import io.datarouter.web.exception.WebExceptionCategory;
import io.datarouter.web.handler.BaseHandler;
import io.datarouter.web.monitoring.GitProperties;
import io.datarouter.web.monitoring.exception.ExceptionAndHttpRequestDto;
import io.datarouter.web.util.ExceptionTool;
import io.datarouter.web.util.RequestAttributeTool;
import io.datarouter.web.util.http.IpAddressService;
import jakarta.inject.Inject;

public class DefaultExceptionRecorder implements ExceptionRecorder{
	private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionRecorder.class);

	@Inject
	private GitProperties gitProperties;
	@Inject
	private ExceptionRecordService exceptionRecordService;
	@Inject
	private ExceptionDetailsDetector exceptionDetailsDetector;
	@Inject
	private DatarouterWebSettingRoot datarouterWebSettingRoot;
	@Inject
	private CurrentSessionInfo currentSessionInfo;
	@Inject
	private WebappName webappName;
	@Inject
	private DatarouterExceptionSettingRoot settings;
	@Inject
	private DatarouterExceptionBuffers exceptionBuffers;
	@Inject
	private ServerName serverName;
	@Inject
	private ServiceName serviceName;
	@Inject
	private EnvironmentName environmentName;
	@Inject
	private IpAddressService ipAddressService;

	@Override
	public Optional tryRecordException(Throwable exception, String callOrigin){
		return tryRecordException(exception, callOrigin, UnknownExceptionCategory.UNKNOWN);
	}

	@Override
	public Optional tryRecordException(
			Throwable exception,
			String callOrigin,
			ExceptionCategory category){
		return tryRecordException(exception, callOrigin, category, List.of());
	}

	@Override
	public Optional tryRecordException(
			Throwable exception,
			String callOrigin,
			ExceptionCategory category,
			List additionalEmailRecipients){
		try{
			ExceptionRecorderDetails exceptionDetails = exceptionDetailsDetector.detect(exception, callOrigin,
					datarouterWebSettingRoot.stackTraceHighlights.get());
			return Optional.of(recordException(
					exception,
					category,
					exceptionDetails.className, // location
					exceptionDetails.methodName,
					exceptionDetails.parsedName,
					exceptionDetails.type,
					exceptionDetails.lineNumber,
					callOrigin,
					additionalEmailRecipients));
		}catch(Exception e){
			logger.warn("Exception while recording an exception", e);
		}
		return Optional.empty();
	}

	@Override
	public ExceptionRecordDto recordException(
			Throwable exception,
			ExceptionCategory category,
			String location,
			String methodName,
			String name,
			String type,
			Integer lineNumber,
			String callOrigin){
		return recordException(exception, category, location, methodName, name, type, lineNumber, callOrigin, List
				.of());
	}

	@Override
	public ExceptionRecordDto recordException(
			Throwable exception,
			ExceptionCategory category,
			String location,
			String methodName,
			String name,
			String type,
			Integer lineNumber,
			String callOrigin,
			List additionalEmailRecipients){
		return recordException(
				category,
				location,
				methodName,
				name,
				type,
				lineNumber,
				callOrigin,
				ExceptionTool.getStackTraceAsString(exception),
				additionalEmailRecipients);
	}

	private ExceptionRecordDto recordException(
			ExceptionCategory category,
			String location,
			String methodName,
			String name,
			String type,
			Integer lineNumber,
			String callOrigin,
			String stackTrace,
			List additionalEmailRecipients){
		if(callOrigin == null){
			callOrigin = location;
		}
		ExceptionCounters.inc(category.name());
		ExceptionCounters.inc(category.name() + " " + webappName);
		ExceptionCounters.inc("name " + name);
		ExceptionCounters.inc(type);
		ExceptionCounters.inc(callOrigin);
		ExceptionCounters.inc(category.name() + " origin " + callOrigin);
		ExceptionCounters.inc(type + " " + callOrigin);
		var exceptionRecord = new ExceptionRecord(
				ExceptionRecordKey.generate(),
				System.currentTimeMillis(),
				serviceName.get(),
				serverName.get(),
				category.name(),
				Optional.ofNullable(name).orElse(ExceptionRecorderDetails.getDefaultName(type, name, callOrigin)),
				stackTrace,
				type,
				gitProperties.getIdAbbrev().orElse(GitProperties.UNKNOWN_STRING),
				location,
				methodName,
				lineNumber,
				callOrigin,
				additionalEmailRecipients,
				environmentName.get());
		exceptionRecord.trimFields();
		if(settings.saveRecordsLocally.get()){
			exceptionBuffers.exceptionRecordBuffer.offer(exceptionRecord);
			logger.warn("Exception recorded ({})", exceptionRecordService.buildExceptionLinkForCurrentServer(
					exceptionRecord));
		}
		if(settings.publishRecords.get()){
			exceptionBuffers.exceptionRecordPublishingBuffer.offer(exceptionRecord);
			if(!settings.saveRecordsLocally.get()){
				logger.warn("Exception recorded with id {}", exceptionRecord.getKey().getId());
			}
		}
		return exceptionRecord.toDto();
	}

	@Override
	public Optional tryRecordExceptionAndHttpRequest(
			Throwable exception,
			String callOrigin,
			HttpServletRequest request){
		try{
			ExceptionRecorderDetails exceptionDetails = exceptionDetailsDetector.detect(exception, callOrigin,
					datarouterWebSettingRoot.stackTraceHighlights.get());
			return Optional.of(recordExceptionAndHttpRequest(
					exception,
					exceptionDetails.className,
					exceptionDetails.methodName,
					exceptionDetails.parsedName,
					exceptionDetails.type,
					exceptionDetails.lineNumber,
					request,
					callOrigin));
		}catch(Exception e){
			logger.warn("Exception while recording an exception", e);
			return Optional.empty();
		}
	}

	@Override
	public ExceptionRecordDto recordExceptionAndHttpRequest(
			Throwable exception,
			String location,
			String methodName,
			String name,
			String type,
			Integer lineNumber,
			HttpServletRequest request,
			String callOrigin){
		ExceptionRecordDto exceptionRecord = recordException(
				exception,
				WebExceptionCategory.HTTP_REQUEST,
				location,
				methodName,
				name,
				type,
				lineNumber,
				callOrigin);
		recordHttpRequest(request, exceptionRecord, true);
		return exceptionRecord;
	}

	@Override
	public ExceptionRecordDto recordExceptionAndHttpRequest(ExceptionAndHttpRequestDto exceptionDto,
			ExceptionCategory category){
		ExceptionRecordDto exceptionRecordDto = recordException(
				category,
				exceptionDto.errorLocation,
				exceptionDto.methodName,
				exceptionDto.name,
				exceptionDto.errorClass,
				exceptionDto.lineNumber,
				exceptionDto.callOrigin,
				exceptionDto.stackTrace,
				List.of());
		recordHttpRequest(exceptionDto, exceptionRecordDto, true);
		return exceptionRecordDto;
	}

	@Override
	public void recordHttpRequest(HttpServletRequest request){
		recordHttpRequest(request, null, false);
	}

	private void recordHttpRequest(
			HttpServletRequest request,
			ExceptionRecordDto exceptionRecord,
			boolean publish){
		Optional userToken = currentSessionInfo.getSession(request).map(Session::getUserToken);
		String userRoles = currentSessionInfo.getRoles(request).toString();

		boolean omitPayload = RequestAttributeTool.get(request, Dispatcher.TRANSMITS_PII)
					.orElse(false);
		String ip = ipAddressService.getIpAddress(request);
		var httpRequestRecord = new HttpRequestRecord(
				exceptionRecord == null ? null : exceptionRecord.id(),
				RequestAttributeTool.get(request, BaseHandler.TRACE_CONTEXT),
				request,
				userRoles,
				userToken.orElse(null),
				omitPayload,
				ip);
		saveAndPublishHttpRequest(httpRequestRecord, publish);
	}

	private void recordHttpRequest(
			ExceptionAndHttpRequestDto exceptionDto,
			ExceptionRecordDto exceptionRecord,
			boolean publish){
		HttpRequestRecord httpRequestRecord = new HttpRequestRecord(exceptionDto, exceptionRecord.id());
		saveAndPublishHttpRequest(httpRequestRecord, publish);
	}

	private void saveAndPublishHttpRequest(HttpRequestRecord httpRequestRecord, boolean publish){
		httpRequestRecord.trimFields();
		if(settings.saveRecordsLocally.get()){
			exceptionBuffers.httpRequestRecordBuffer.offer(httpRequestRecord);
		}
		httpRequestRecord.trimBinaryBody(ExceptionInstrumentationConstants.MAX_SIZE_BINARY_BODY);
		if(publish && settings.publishRecords.get()){
			exceptionBuffers.httpRequestRecordPublishingBuffer.offer(httpRequestRecord);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy