MST

星途 面试题库

面试题:Java日志框架扩展与定制

现有一个复杂的分布式Java项目,内置日志框架功能无法满足特定需求,如需要将日志以特定加密格式发送到远程服务器。请阐述如何基于现有主流日志框架(如SLF4J结合Logback)进行扩展和定制来实现该需求,包括涉及到的技术点和实现步骤。
30.5万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

技术点

  1. 自定义Appender:Logback允许通过继承AppenderBase类来创建自定义的Appender,用于将日志发送到远程服务器。
  2. 加密处理:使用Java的加密库(如JCE,Java Cryptography Architecture)对日志内容进行加密。
  3. 网络通信:利用Java的网络编程技术(如Socket、HTTP Client)将加密后的日志发送到远程服务器。
  4. SLF4J绑定:确保自定义的Logback Appender能与SLF4J正确绑定,使项目中的日志调用能使用到新的功能。

实现步骤

  1. 创建自定义Appender类
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import ch.qos.logback.core.AppenderBase;
    
    public class CustomEncryptedAppender extends AppenderBase<ILoggingEvent> {
        @Override
        protected void append(ILoggingEvent eventObject) {
            // 这里开始处理日志
        }
    }
    
  2. 加密日志内容
    import javax.crypto.Cipher;
    import javax.crypto.KeyGenerator;
    import javax.crypto.SecretKey;
    import java.nio.charset.StandardCharsets;
    import java.security.SecureRandom;
    
    public class EncryptionUtil {
        private static final String ALGORITHM = "AES";
        private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
    
        public static byte[] encrypt(String data, SecretKey secretKey) throws Exception {
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        }
    
        public static SecretKey generateSecretKey() throws Exception {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
            keyGenerator.init(128, new SecureRandom());
            return keyGenerator.generateKey();
        }
    }
    
  3. 发送加密日志到远程服务器
    • 使用Socket
    import java.io.OutputStream;
    import java.net.Socket;
    
    public class RemoteSender {
        public static void send(byte[] encryptedData, String serverAddress, int port) throws Exception {
            try (Socket socket = new Socket(serverAddress, port)) {
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write(encryptedData);
            }
        }
    }
    
    • 使用HTTP Client(以Apache HttpClient为例)
    import org.apache.http.HttpEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.ByteArrayEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    
    public class HttpRemoteSender {
        public static void send(byte[] encryptedData, String serverUrl) throws Exception {
            try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                HttpPost httpPost = new HttpPost(serverUrl);
                HttpEntity entity = new ByteArrayEntity(encryptedData);
                httpPost.setEntity(entity);
                try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                    EntityUtils.consume(response.getEntity());
                }
            }
        }
    }
    
  4. 在自定义Appender中整合加密与发送逻辑
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import ch.qos.logback.core.AppenderBase;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class CustomEncryptedAppender extends AppenderBase<ILoggingEvent> {
        private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
        private String serverAddress;
        private int port;
        private String serverUrl; // 如果使用HTTP
    
        public void setServerAddress(String serverAddress) {
            this.serverAddress = serverAddress;
        }
    
        public void setPort(int port) {
            this.port = port;
        }
    
        public void setServerUrl(String serverUrl) {
            this.serverUrl = serverUrl;
        }
    
        @Override
        protected void append(ILoggingEvent eventObject) {
            executorService.submit(() -> {
                try {
                    String logMessage = eventObject.getFormattedMessage();
                    SecretKey secretKey = EncryptionUtil.generateSecretKey();
                    byte[] encryptedData = EncryptionUtil.encrypt(logMessage, secretKey);
                    if (serverUrl != null) {
                        HttpRemoteSender.send(encryptedData, serverUrl);
                    } else {
                        RemoteSender.send(encryptedData, serverAddress, port);
                    }
                } catch (Exception e) {
                    addError("Failed to send encrypted log", e);
                }
            });
        }
    }
    
  5. 配置Logbacklogback.xml中添加自定义Appender的配置:
<configuration>
    <appender name="CUSTOM_ENCRYPTED" class="com.example.CustomEncryptedAppender">
        <serverAddress>your_server_address</serverAddress>
        <port>your_port</port>
        <!-- 如果使用HTTP -->
        <serverUrl>your_http_server_url</serverUrl>
    </appender>
    <root level="info">
        <appender-ref ref="CUSTOM_ENCRYPTED" />
    </root>
</configuration>
  1. 确保SLF4J绑定 项目中已经使用SLF4J结合Logback,只要自定义的Logback Appender配置正确,SLF4J就能自动绑定并使用新的Appender来处理日志。例如,项目中的日志调用方式不变:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SomeClass {
    private static final Logger logger = LoggerFactory.getLogger(SomeClass.class);

    public void someMethod() {
        logger.info("This is a log message");
    }
}