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

com.amazon.redshift.logger.RedshiftLogger Maven / Gradle / Ivy

The newest version!
package com.amazon.redshift.logger;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.DriverManager;
import java.text.FieldPosition;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.Locale;

import com.amazon.redshift.RedshiftProperty;
import com.amazon.redshift.util.RedshiftProperties;

/**
 * Logger for each connection or at driver.
 * 
 * @author iggarish
 *
 */
public class RedshiftLogger {
	
	// Any connection enable the logging
	private static boolean isEnable = false;
	
	private static RedshiftLogger driverLogger;
	
  private volatile LogLevel level = LogLevel.OFF;
  
  private String fileName;

  private LogHandler handler;
  
  private static AtomicInteger  connectionId = new AtomicInteger();

  public RedshiftLogger(String fileName,
  											String logLevel, 
  											boolean driver,
  											String maxLogFileSize,
  											String maxLogFileCount) {
  	
  	if (driver) {
  		this.fileName = fileName;
  		driverLogger = this;
  	}
  	else {
  		int connId = connectionId.incrementAndGet();

  		if (fileName != null) {
        String fileExt = "";
        
        if (fileName.contains("."))
        {
            fileExt = fileName
                .substring(fileName.lastIndexOf("."), fileName.length());
            fileName = fileName.substring(0, fileName.lastIndexOf("."));
        }
        
  			
  			this.fileName = fileName + "_connection_" + connId 
  												+ fileExt ;
  		}
  	}
  	
  	level = (logLevel != null )
  						? LogLevel.getLogLevel(logLevel)
  						: LogLevel.OFF;
  	
  	if (level != LogLevel.OFF) {
  		try {
	      if (DriverManager.getLogWriter() != null) {
	      	handler = new LogWriterHandler(DriverManager.getLogWriter());
	      }
	      else 
	      if (this.fileName != null) {
	      	handler = new LogFileHandler(this.fileName, driver, maxLogFileSize, maxLogFileCount);
	      }
	      else {
	  			handler = new LogConsoleHandler();
	      }
  		}
  		catch(Exception ex) {
  			handler = new LogConsoleHandler();
  		}
      
      isEnable = true;
  	}
  }
  /**
   * Check for logging enable or not.
   * 
   * @return true if any of the connection enable the logging, false otherwise.
   */
  public static boolean isEnable() {
  	return isEnable;
  }
  
  public static RedshiftLogger getDriverLogger() {
  	return driverLogger;
  }

  /** True if logger associated with the connection enable the logging.
   *  Otherwise false.
   *  
   *  One logger associated with each connection.
   *  There is a separate logger at driver level.
   *  
   * @return
   */
  private boolean isEnabled() {
  	return (level != LogLevel.OFF);
  }
  
  public LogLevel getLogLevel() {
  	return level;
  }
  
  /**
   * Determines if logging should occur based on the LogLevel.
   *
   * @param level
   *          The level of logging to attempt.
   * @param log
   *          The ILogger to try to log to.
   *
   * @return  True if logging is to be done according to LogLevel; false otherwise.
   */
  public static boolean checkLogLevel(LogLevel level, RedshiftLogger log)
  {
    return (log.isEnabled()) &&
           (level.ordinal() <= log.getLogLevel().ordinal());
  }

/*  public static boolean isLoggable(LogLevel level, RedshiftLogger log) {
  	return checkLogLevel(level, log);
  } */
  
  private static StackTraceElement getStackElementAbove(String functionName)
  {
    boolean returnNextFunction = false;

    // Look for the function above the specified one.
    for (StackTraceElement s : Thread.currentThread().getStackTrace())
    {
        if (returnNextFunction)
        {
            return s;
        }
        else if (s.getMethodName().equals(functionName))
        {
            returnNextFunction = true;
        }
    }

    // Default to just returning 3 above, which should be the caller of the caller of this
    // function.
    return Thread.currentThread().getStackTrace()[3];
  }
  
  public static String maskSecureInfoInUrl(String url)
  {
	  String[] tokens = {
			  RedshiftProperty.PWD.getName(),
			  RedshiftProperty.PASSWORD.getName(),
			  RedshiftProperty.IAM_ACCESS_KEY_ID.getName(),
			  RedshiftProperty.IAM_SECRET_ACCESS_KEY.getName(),
			  RedshiftProperty.IAM_SESSION_TOKEN.getName(),
			  RedshiftProperty.AUTH_PROFILE.getName(),
			  RedshiftProperty.SSL_KEY.getName(),
			  RedshiftProperty.SSL_PASSWORD.getName(),
			  RedshiftProperty.WEB_IDENTITY_TOKEN.getName(),
			  "Client_ID",
			  "Client_Secret",
			  "IdP_Tenant",
			  "Partner_SPID",
			  "Preferred_Role",
			  "Profile",
			  "roleArn",
	  };

		String temp = maskSecureInfo(url, tokens, "[\\?;&]");
		
		return temp;
  }
  
  public static String maskSecureInfo(String msg, String[] tokens, String tokenizer) {
  	if(msg == null) return msg;
  	StringBuilder newMsg = new StringBuilder();
  	String[] splitMsgs = msg.split(tokenizer);
  	boolean secureInfoFound = false;
  	
  	for(String splitMsg : splitMsgs) {
  		String tokenFound = null;
			String sTemp = splitMsg.toLowerCase();
  		for(String token : tokens) {
  			String temp = token.toLowerCase();
  			if(sTemp.contains(temp)) {
  				tokenFound = token;
  				secureInfoFound = true;
  				break;
  			}
  		}
  		
  		if(tokenFound == null) {
  			newMsg.append(splitMsg).append(";");
  		}
  		else {
  			newMsg.append(tokenFound).append("=***;");
  		}
  	}
  	
  	return (secureInfoFound) ? newMsg.toString() : msg;
  }

  public static Properties maskSecureInfoInProps(Properties info) {
  	
  	if(info == null) return null;

		String[] propNames = {
				RedshiftProperty.PWD.getName(),
				RedshiftProperty.PASSWORD.getName(),
				RedshiftProperty.IAM_ACCESS_KEY_ID.getName(),
				RedshiftProperty.IAM_SECRET_ACCESS_KEY.getName(),
				RedshiftProperty.IAM_SESSION_TOKEN.getName(),
				RedshiftProperty.AUTH_PROFILE.getName(),
				RedshiftProperty.SSL_KEY.getName(),
				RedshiftProperty.SSL_PASSWORD.getName(),
				RedshiftProperty.WEB_IDENTITY_TOKEN.getName(),
				"Client_ID",
				"Client_Secret",
				"IdP_Tenant",
				"Partner_SPID",
				"Preferred_Role",
				"Profile",
				"roleArn",
		};
		
		Properties loggedProperties = new RedshiftProperties();
	  	loggedProperties.putAll(info);
	  	removeUnrecognizedPropertiesFromLogging(loggedProperties);

		for(String propName : propNames)
		{
			replaceIgnoreCase(loggedProperties, propName, "***");
		}
		
		return loggedProperties;
  }

	/**
	 * fetches set of properties defined in the RedshiftProperty enum class and properties defined in public docs
	 * compares against given properties and removes unrecognized properties from logs
	 */
  public static void removeUnrecognizedPropertiesFromLogging(Properties loggedProperties)
  {
	  Set enumProperties = Arrays.stream(RedshiftProperty.values()).map(x -> x.getName().toLowerCase(Locale.US)).collect(Collectors.toSet());
	  Set publicProperties = RedshiftProperty.getPublicProperties().stream().map(x -> x.toLowerCase(Locale.US)).collect(Collectors.toSet());

	  Set allProperties = enumProperties.stream().collect(Collectors.toSet());
	  allProperties.addAll(publicProperties);

	  for (String givenProperty : loggedProperties.stringPropertyNames())
	  {
		  if (!allProperties.contains(givenProperty))
		  {
			  loggedProperties.remove(givenProperty);
		  }
	  }
  }


  public static String replaceIgnoreCase(Properties info, String key, String newVal) {
    String value = info.getProperty(key);
    if (null != value) {
      info.replace(key, newVal);
      return value;
    }

    // Not matching with the actual key then
    Set> s = info.entrySet();
    Iterator> it = s.iterator();
    while (it.hasNext()) {
     Entry entry = it.next();
     if (key.equalsIgnoreCase((String) entry.getKey())) {
     	info.replace(key, newVal);
      return (String) entry.getValue();
     }
    }
    return null;
   }  
  
  private static String[] getCallerMethodName(String logFunction)
  {
    /*
     * Stack Trace:
     * 0 - dumpThreads
     * 1 - getStackTrace
     * 2 - current method (logging)
     * 3 - calling method (the one we want to log)
     * 4 - method calling the method we want to log... etc.
     */

    // Retrieve the information necessary to log the message.
    StackTraceElement element = getStackElementAbove(logFunction);
    String[] names = new String[3];
    names[2] = element.getMethodName();

    try
    {
        // Dynamically look up the name of the class.
        Class originatingClass = Class.forName(element.getClassName());
        names[1] = originatingClass.getSimpleName();

        // Get the package of the class.
        names[0] = "";
        Package originatingPackage = originatingClass.getPackage();
        if (null != originatingPackage)
        {
            names[0] = originatingPackage.getName();
        }
    }
    catch (ClassNotFoundException e)
    {
        // Failed to look up the class, just omit it.
        names[0] = "";
        names[1] = element.getClassName();
    }

    if (names[2].equals(""))
    {
        //  means the constructor, so just use the class name.
        names[2] = names[1];
    }

    return names;
  }

  public void log(LogLevel logLevel, String msg, Object... msgArgs) {
  	
  	if (!checkLogLevel(logLevel, this))
      return;

	  // Get the package, class, and method names.
	  String[] callerNames = getCallerMethodName("log");
  	
  	logMsg(logLevel, callerNames, msg, msgArgs);
  }

  public void log(LogLevel logLevel, Throwable thrown, String msg, Object... msgArgs) {
  	
  	if (!checkLogLevel(logLevel, this))
      return;

	  // Get the package, class, and method names.
	  String[] callerNames = getCallerMethodName("log");
  	
	  StringWriter sw = new StringWriter();
	  thrown.printStackTrace(new PrintWriter(sw));
	  String stacktrace = sw.toString();	  
	  
  	logMsg(logLevel, callerNames, msg, msgArgs);
  	logMsg(logLevel, callerNames, stacktrace);
  }
  
  public void logError(Exception error) {
  	
  	if (!checkLogLevel(LogLevel.ERROR, this))
      return;

	  // Get the package, class, and method names.
	  String[] callerNames = getCallerMethodName("logError");
	  
	  StringWriter sw = new StringWriter();
	  error.printStackTrace(new PrintWriter(sw));
	  String stacktrace = sw.toString();	  
  	
  	logMsg(LogLevel.ERROR, callerNames, stacktrace);
  }
  
  public void logError(String msg, Object... msgArgs) {
  	
  	if (!checkLogLevel(LogLevel.ERROR, this))
      return;

	  // Get the package, class, and method names.
	  String[] callerNames = getCallerMethodName("logError");
  	
  	logMsg(LogLevel.ERROR, callerNames, msg, msgArgs);
  }

  public void logInfo(String msg, Object... msgArgs) {
  	
  	if (!checkLogLevel(LogLevel.INFO, this))
      return;

	  // Get the package, class, and method names.
	  String[] callerNames = getCallerMethodName("logInfo");
  	
  	logMsg(LogLevel.INFO, callerNames, msg, msgArgs);
  }

  public void logFunction(boolean entry, Object... params) {
  	
  	if (!checkLogLevel(LogLevel.FUNCTION, this))
      return;
  	
  	String msg = (entry) ? " Enter " : " Return ";

  	if (params != null) {
  		StringBuffer paramVal = new StringBuffer();
  		int paramCount = 0;

			paramVal.append(msg);
			
  		if (entry)
  			paramVal.append("(");
  		
  		for (Object param : params) {
  			if (paramCount++ != 0)
  				paramVal.append(",");

  			if(param != null) {
  				if(param.getClass().isArray()) {
  					if(param instanceof Object[])
  						paramVal.append(Arrays.toString((Object[])param));
  					else
  					if(param instanceof int[])
  						paramVal.append(Arrays.toString((int[])param));
  					else
  					if(param instanceof long[])
  						paramVal.append(Arrays.toString((long[])param));
  					else
      				paramVal.append(param);
  				}
  				else
    				paramVal.append(param);
  			}
  			else
  				paramVal.append(param);
  		}
  		
  		if (entry)
  			paramVal.append(") ");
  		else
  			paramVal.append(" ");
  		
  		msg = paramVal.toString();
  	}
  	
	  // Get the package, class, and method names.
	  String[] callerNames = getCallerMethodName("logFunction");
  	
  	logMsg(LogLevel.FUNCTION, callerNames, msg);
  }
  
  public void logDebug(String msg, Object... msgArgs) {
  	
  	if (!checkLogLevel(LogLevel.DEBUG, this))
      return;

	  // Get the package, class, and method names.
	  String[] callerNames = getCallerMethodName("logDebug");
  	
  	logMsg(LogLevel.DEBUG, callerNames, msg, msgArgs);
  }
  
  public void close() {
  	if (handler != null
  			&& handler instanceof LogFileHandler) {
  		try {
				handler.close();
			} catch (Exception e) {
				// Ignore it.
			}
  	}
  }
  
  public void flush() {
  	if (handler != null)
  			handler.flush();
  }
  
  private void logMsg(LogLevel level, String[] callerNames,
  										String msg, Object... msgArgs) {
    // Log the message.
    String formattedMsg = formatLogMsg(
											        level,
											        callerNames[0],
											        callerNames[1],
											        callerNames[2],
											        msg,
											        msgArgs);
    
    try {
	    if (formattedMsg != null
	    		&& null != handler) {
	    	handler.write(formattedMsg);
	    }
    }
    catch(Exception ex) {
      throw new RuntimeException(ex);
    }
  }
  
  private String formatLogMsg(
      LogLevel logLevel,
      String packageName,
      String className,
      String methodName,
      String msg,
      Object... msgArgs)
  {
    if (null == handler)
        return null;

    StringBuffer msgBuf = new StringBuffer();
    SimpleDateFormat dateFormat = null;
	
    dateFormat = new SimpleDateFormat("MMM dd HH:mm:ss.SSS");
	
    dateFormat.format(new Date(), msgBuf, new FieldPosition(0));
    msgBuf.append(" ");
    msgBuf.append(logLevel.toString()).append(" ");
    msgBuf.append(" ");
	
    msgBuf.append("[").append(Thread.currentThread().getId()).append(" ").append(Thread.currentThread().getName()).append("] ");

    msgBuf.append(packageName).append(".");
    msgBuf.append(className).append(".");
    msgBuf.append(methodName).append(": ");

    if (msgArgs == null || msgArgs.length == 0)
    	msgBuf.append(msg);
    else
    	msgBuf.append(new MessageFormat(msg).format(msgArgs));

    return msgBuf.toString();
  }
  
  public static String getLogFileUsingPath(String logLevel, String logPath) {
  	if (logPath == null) {
  		// Check loglevel and get current directory
    	LogLevel level = (logLevel != null )
					? LogLevel.getLogLevel(logLevel)
					: LogLevel.OFF;
			if (level != LogLevel.OFF)
				logPath = System.getProperty("user.dir");
  	}
  	
  	if (logPath != null) {
  		return logPath + File.separatorChar + "redshift_jdbc.log";
  	}
  	else {
  		return null;
  	}
  }
  
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy