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

com.stackify.error.log4j12.StackifyErrorAppender Maven / Gradle / Ivy

/*
 * Copyright 2013 Stackify
 *
 * 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 com.stackify.error.log4j12;

import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;

import com.stackify.api.ApiClient;
import com.stackify.api.EnvironmentDetail;
import com.stackify.api.StackifyError;
import com.stackify.api.common.ApiClients;
import com.stackify.api.common.EnvironmentDetails;
import com.stackify.api.common.error.ErrorGovernor;
import com.stackify.api.common.http.StackifyErrorSender;
import com.stackify.api.common.lang.Throwables;
import com.stackify.api.json.StackifyErrorConverter;

/**
 * Log4j 1.2 logger appender for sending exceptions to Stackify.
 * 
 * 

* Example (*.properties file): *

 * {@code
 * log4j.appender.STACKIFY_ERROR=com.stackify.error.log4j12.StackifyErrorAppender
 * log4j.appender.STACKIFY_ERROR.apiKey=YOUR_API_KEY
 * log4j.appender.STACKIFY_ERROR.application=YOUR_APPLICATION_NAME
 * log4j.appender.STACKIFY_ERROR.environment=YOUR_ENVIRONMENT
 * }
 * 
* *

* Example (*.xml file): *

 * {@code
 * 
 *     
 *     
 *     
 * 
 * }
 * 
* * @author Eric Martin */ public class StackifyErrorAppender extends NonReentrantAppender { /** * Logger for the appender */ private static final Logger LOGGER = Logger.getLogger(StackifyErrorAppender.class); /** * Client side error governor to suppress duplicate errors */ private ErrorGovernor errorGovernor = new ErrorGovernor(); /** * The class responsible for sending the errors to Stackify */ private StackifyErrorSender errorSender; /** * The API Client */ private ApiClient apiClient = null; /** * The environment detailassertNull */ private EnvironmentDetail environmentDetail = null; /** * API URL (Appender configuration parameter) */ private String apiUrl = "https://api.stackify.com/Error/V1"; /** * API Key (Appender configuration parameter) */ private String apiKey = null; /** * Application name (Appender configuration parameter) */ private String application = null; /** * Environment (Appender configuration parameter) */ private String environment = null; /** * JSON Converter (Appender configuration parameter) */ private String converter = "com.stackify.api.json.jackson.StackifyErrorJacksonConverter"; /** * @return the apiUrl */ public String getApiUrl() { return apiUrl; } /** * @param apiUrl the apiUrl to set */ public void setApiUrl(String apiUrl) { this.apiUrl = apiUrl; } /** * @return the apiKey */ public String getApiKey() { return apiKey; } /** * @param apiKey the apiKey to set */ public void setApiKey(String apiKey) { this.apiKey = apiKey; } /** * @return the application */ public String getApplication() { return application; } /** * @param application the application to set */ public void setApplication(String application) { this.application = application; } /** * @return the environment */ public String getEnvironment() { return environment; } /** * @param environment the environment to set */ public void setEnvironment(String environment) { this.environment = environment; } /** * @return the converter */ public String getConverter() { return converter; } /** * @param converter the converter to set */ public void setConverter(String converter) { this.converter = converter; } /** * @return the apiClient */ public ApiClient getApiClient() { return apiClient; } /** * @return the environmentDetail */ public EnvironmentDetail getEnvironmentDetail() { return environmentDetail; } /** * @return the errorSender */ public StackifyErrorSender getErrorSender() { return errorSender; } /** * @see org.apache.log4j.AppenderSkeleton#activateOptions() */ @Override public void activateOptions() { super.activateOptions(); // check the api key if ((apiKey == null) || (apiKey.isEmpty())) { LOGGER.error("API Key not set for the Stackify Error Appender"); return; } // build the static API client record this.apiClient = ApiClients.getApiClient(StackifyErrorAppender.class, "/stackify-error-log4j12.properties"); // build the static environment details record this.environmentDetail = EnvironmentDetails.getEnvironmentDetail(application, environment); // load the JSON converter try { Class converterClass = Class.forName(converter); StackifyErrorConverter converterInstance = (StackifyErrorConverter) converterClass.newInstance(); errorSender = new StackifyErrorSender(converterInstance); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } /** * @see com.stackify.error.log4j12.NonReentrantAppender#subAppend(org.apache.log4j.spi.LoggingEvent) */ @Override protected void subAppend(final LoggingEvent event) { // Make sure the API Key is set if ((apiKey == null) || (apiKey.isEmpty())) { return; } // Get the exception from the event // If it was a FATAL or ERROR without an exception, create a placeholder for the stack trace Throwable exception = getThrowable(event); if (exception == null) { if ((event.getLevel() == Level.ERROR) || (event.getLevel() == Level.FATAL)) { exception = new Throwable(); } else { return; } } try { // Build the tags for the error event List tags = new ArrayList(); tags.add("log4j12"); tags.add(event.getLevel().toString().toLowerCase()); tags.add(event.getLoggerName()); // Build the StackifyError POJO from the LoggingEvent StackifyError.Builder errorBuilder = StackifyError.newBuilder(); errorBuilder.apiClient(apiClient); errorBuilder.environmentDetail(environmentDetail); errorBuilder.occurredEpochMillis(new Date(event.getTimeStamp())); errorBuilder.error(Throwables.toErrorItem(getLogMessage(event), exception)); errorBuilder.tags(tags); Map customProperties = getCustomProperties(event); if (!customProperties.isEmpty()) { errorBuilder.customProperties(customProperties); } StackifyError error = errorBuilder.build(); // Send the error to Stackify if (errorGovernor.errorShouldBeSent(error)) { int rc = errorSender.send(apiUrl, apiKey, Collections.singletonList(error)); if (rc != HttpURLConnection.HTTP_OK) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Stackify Error Service returned HTTP " + Integer.toString(rc)); } } } } catch (Throwable t) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Exception posting to Stackify Error Service", t); } } } /** * @see org.apache.log4j.Appender#close() */ @Override public void close() { } /** * @see org.apache.log4j.Appender#requiresLayout() */ @Override public boolean requiresLayout() { return false; } /** * Returns the exception tied to this log event * @param event The log event * @return The Throwable or null */ private Throwable getThrowable(final LoggingEvent event) { ThrowableInformation throwableInfo = event.getThrowableInformation(); if (throwableInfo != null) { Throwable t = throwableInfo.getThrowable(); if (t != null) { return t; } } Object message = event.getMessage(); if (message != null) { if (message instanceof Throwable) { return (Throwable) message; } } return null; } /** * Gets custom properties from the events MDC and MDC * @param event The logging event * @return Map assembled from the event's MDC and NDC */ private Map getCustomProperties(final LoggingEvent event) { Map customProperties = new HashMap(); // unload the MDC Map mdc = event.getProperties(); if (mdc != null) { Iterator mdcIterator = mdc.entrySet().iterator(); while (mdcIterator.hasNext()) { Map.Entry entryPair = (Map.Entry) mdcIterator.next(); Object key = entryPair.getKey(); Object value = entryPair.getValue(); customProperties.put(key.toString(), value != null ? value.toString() : null); } } // unload the NDC String ndc = event.getNDC(); if (ndc != null) { if (!ndc.isEmpty()) { customProperties.put("NDC", ndc); } } // return the custom properties return customProperties; } /** * Returns the log message * @param event The log event * @return The log message or null */ private String getLogMessage(final LoggingEvent event) { Object message = event.getMessage(); if (message != null) { if (message instanceof String) { return (String) message; } } return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy