blob: 0f06c26a4ff4f2a55b9d0ba0074d63d6c4a1d963 [file] [log] [blame]
/*
* Licensed to 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 org.apache.servicecomb.foundation.common.utils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.stream.Collectors;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.PropertyConfigurator;
import org.apache.servicecomb.foundation.common.config.impl.PropertiesLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
public final class Log4jUtils {
private static final String MERGED_FILE = "merged.log4j.properties";
public static final String OUTPUT_CONFIG_ENABLED = "log4j.logger.outputConfig.enabled";
public static final String OUTPUT_CONFIG_ENABLED_TRUE = "true";
// spring boot的包装中会重复调用init,需要规避一下
private static boolean inited = false;
private static final Object LOCK = new Object();
private Log4jUtils() {
}
public static void init() throws Exception {
init(Arrays.asList("classpath*:config/base/log4j.properties", "classpath*:config/log4j.properties"));
}
public static void init(String locationPattern) throws Exception {
init(Arrays.asList(locationPattern));
}
public static void init(List<String> locationPatterns) throws Exception {
if (inited) {
return;
}
synchronized (LOCK) {
if (inited) {
return;
}
PropertiesLoader loader = new PropertiesLoader(locationPatterns);
Properties properties = loader.load();
if (properties.isEmpty()) {
throw new Exception("can not find resource " + locationPatterns);
}
PropertyConfigurator.configure(properties);
inited = true;
if (OUTPUT_CONFIG_ENABLED_TRUE.equals(
properties.getProperty(OUTPUT_CONFIG_ENABLED, OUTPUT_CONFIG_ENABLED_TRUE))) {
// If the property file with the highest priority is on a hard disk(not in a jar package)
// and we have write access, output the merged property file for the purpose of debugging
outputFile(loader.getFoundResList(), properties);
}
}
}
@VisibleForTesting
static boolean isInited() {
return inited;
}
private static void outputFile(List<Resource> resList,
Properties properties) throws IOException {
//不可以作为class的变量初始化,因为在outputFile前一句log机制才初始化完成的
//must create org.slf4j.impl.Log4jLoggerAdapter by LoggerExtFactory
//in order to redefine Log4jLoggerAdapter before other class load Log4jLoggerAdapter
Logger log = LoggerFactory.getLogger(Log4jUtils.class);
String content = genFileContext(resList, properties);
//不打印配置信息,有密钥等敏感信息
//log.info("Merged log4j:\n{}", content);
Resource res = resList.get(resList.size() - 1);
// 不能直接使用res.getFile,因为jar里面的资源,getFile会抛异常
File file = new File(res.getURL().getPath());
if (!file.getParentFile().canWrite()) {
log.error("Can not output {},because can not write to directory of file {}",
MERGED_FILE,
res.getURL().getPath());
return;
}
File mergedfile = new File(res.getFile().getParentFile(), MERGED_FILE);
FileUtils.writeStringToFile(mergedfile, content, StandardCharsets.UTF_8, false);
log.info("Write merged log4j config file to {}", IOUtils.anonymousPath(mergedfile.getAbsolutePath()));
}
private static String genFileContext(List<Resource> resList, Properties properties) throws IOException {
List<Entry<Object, Object>> entryList = properties.entrySet()
.stream()
.sorted(Comparator.comparing(o -> o.getKey().toString()))
.collect(Collectors.toList());
StringBuilder sb = new StringBuilder();
for (Resource res : resList) {
sb.append("#").append(res.getURL().getPath()).append("\n");
}
for (Entry<Object, Object> entry : entryList) {
sb.append(entry.getKey()).append("=").append(entry.getValue()).append("\n");
}
return sb.toString();
}
}