/** * Copyright 2015 StreamSets Inc. * * Licensed under the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.streamsets.datacollector.log; import com.google.common.annotations.VisibleForTesting; import com.streamsets.datacollector.main.RuntimeInfo; import com.streamsets.pipeline.api.impl.Utils; import com.streamsets.pipeline.lib.parser.DataParserException; import com.streamsets.pipeline.lib.parser.log.Constants; import com.streamsets.pipeline.lib.parser.log.Log4jHelper; import com.streamsets.pipeline.lib.parser.shaded.org.aicer.grok.dictionary.GrokDictionary; import com.streamsets.pipeline.lib.parser.shaded.org.aicer.grok.util.Grok; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Properties; public class LogUtils { public static final String LOG4J_FILE_ATTR = "log4j.filename"; public static final String LOG4J_APPENDER_STREAMSETS_FILE_PROPERTY = "log4j.appender.streamsets.File"; public static final String LOG4J_APPENDER_STREAMSETS_LAYOUT_CONVERSION_PATTERN = "log4j.appender.streamsets.layout.ConversionPattern"; public static final String LOG4J_APPENDER_STDERR_LAYOUT_CONVERSION_PATTERN = "log4j.appender.stderr.layout.ConversionPattern"; public static final String LOG4J_GROK_ATTR = "log4j.grok"; public static final String LOG4J_CONVERSION_PATTERN = "%d{ISO8601} [user:%X{s-user}] [pipeline:%X{s-entity}] [runner:%X{s-runner}][thread:%t] %-5p %c{1} - %m%n"; private LogUtils() {} public static String getLogFile(RuntimeInfo runtimeInfo) throws IOException { if(Boolean.getBoolean("sdc.transient-env")) { // running under mesos // TODO - find a better way, as this logs all spark stuff under sdc pipeline logs String mesosRootDir = System.getenv("MESOS_DIRECTORY"); if (mesosRootDir!=null) { return new File(mesosRootDir, "stderr").getAbsolutePath(); } // we are running under YARN String logDirs = System.getenv("LOG_DIRS"); if (logDirs == null) { if(Boolean.getBoolean("sdc.testing-mode")) { logDirs = System.getProperty("user.dir") + "/target/"; } else { throw new IllegalStateException("When running in transient environment, environment variable " + "LOG_DIRS must be defined"); } } File syslog = new File(logDirs, "syslog"); if (syslog.isFile()) { return syslog.getAbsolutePath(); } // fall back to stderr return (new File(logDirs, "stderr")).getAbsolutePath(); } String logFile = runtimeInfo.getAttribute(LOG4J_FILE_ATTR); if (logFile == null) { URL log4jConfig = runtimeInfo.getAttribute(RuntimeInfo.LOG4J_CONFIGURATION_URL_ATTR); if (log4jConfig != null) { try (InputStream is = log4jConfig.openStream()) { Properties props = new Properties(); props.load(is); logFile = props.getProperty(LOG4J_APPENDER_STREAMSETS_FILE_PROPERTY); if (logFile != null) { logFile = resolveValue(logFile); } else { throw new IOException(Utils.format( "Property '{}' is not defined in {}. No log file is configured for display.", LOG4J_APPENDER_STREAMSETS_FILE_PROPERTY, log4jConfig )); } if (!logFile.endsWith(".log")) { throw new IOException(Utils.format("Log file '{}' must end with '.log',", logFile)); } runtimeInfo.setAttribute(LOG4J_FILE_ATTR, logFile); } } else { throw new IOException(Utils.format("RuntimeInfo does not has attribute '{}'", RuntimeInfo.LOG4J_CONFIGURATION_URL_ATTR)); } } return logFile; } public static Grok getLogGrok(RuntimeInfo runtimeInfo) throws IOException, DataParserException { Grok logFileGrok = runtimeInfo.getAttribute(LOG4J_GROK_ATTR); String logPattern; if(logFileGrok == null) { if(Boolean.getBoolean("sdc.transient-env")) { // hack for Worker SDC until we use single log4j property for Standalone and Worker SDC logPattern = LOG4J_CONVERSION_PATTERN; } else { URL log4jConfig = runtimeInfo.getAttribute(RuntimeInfo.LOG4J_CONFIGURATION_URL_ATTR); if (log4jConfig != null) { try (InputStream is = log4jConfig.openStream()) { Properties props = new Properties(); props.load(is); logPattern = props.getProperty(LOG4J_APPENDER_STREAMSETS_LAYOUT_CONVERSION_PATTERN); if (logPattern == null) { logPattern = props.getProperty(LOG4J_APPENDER_STDERR_LAYOUT_CONVERSION_PATTERN); } } } else { throw new IOException(Utils.format("RuntimeInfo does not has attribute '{}'", RuntimeInfo.LOG4J_CONFIGURATION_URL_ATTR)); } } if (logPattern != null) { String grokPattern = Log4jHelper.translateLog4jLayoutToGrok(logPattern); GrokDictionary grokDictionary = new GrokDictionary(); try( InputStream grogPatterns = LogUtils.class.getClassLoader().getResourceAsStream(Constants.GROK_PATTERNS_FILE_NAME); InputStream javaPatterns = LogUtils.class.getClassLoader().getResourceAsStream(Constants.GROK_JAVA_LOG_PATTERNS_FILE_NAME); ) { grokDictionary.addDictionary(grogPatterns); grokDictionary.addDictionary(javaPatterns); } grokDictionary.bind(); logFileGrok = grokDictionary.compileExpression(grokPattern); runtimeInfo.setAttribute(LOG4J_GROK_ATTR, logFileGrok); } else { throw new IllegalStateException("Cannot find log4j layout conversion pattern"); } } return logFileGrok; } public static File[] getLogFiles(RuntimeInfo runtimeInfo) throws IOException { String logFile = LogUtils.getLogFile(runtimeInfo); File log = new File(logFile); File logDir = log.getParentFile(); final String logName = log.getName(); return logDir.listFiles((dir, name) -> name.startsWith(logName)); } @VisibleForTesting static String resolveValue(String str) { while (str.contains("${")) { int start = str.indexOf("${"); int end = str.indexOf("}", start); String value = System.getProperty(str.substring(start + 2, end)); String current = str; str = current.substring(0, start) + value + current.substring(end + 1); } return str; } }