[JAVA] Log out a CSV file that can be read in Excel using logback

Introduction

We received a request from a user to analyze and audit the usage status of Web applications. Originally I used logback for log output, so I decided to support it by outputting a CSV file from logback.

What I made

--Outputs a UTF-8 CSV file with BOM. --In Excel, when you open a UTF-8 CSV file without BOM, the characters are garbled. --Add a fixed wording header at the beginning of the file to make it easier to read from Excel. --Log files are rotated on a monthly basis.

Realization method

I made it by referring to Add an expression at the start of every log file using Logback --Stack Overflow.

Create your own Appender

Prepare an Appender that puts the BOM and fixed wording at the beginning of the file.

AuthFileAppender.java


public class AuthFileAppender extends RollingFileAppender {
    @Override
    public void openFile(String fileName) throws IOException {
        super.openFile(fileName);
        File activeFile = new File(getFile());
        if (activeFile.exists() && activeFile.isFile() && activeFile.length() == 0) {
            //Output BOM and header only when the output target is an empty file
            lock.lock();
            try {
                OutputStream outputStream = super.getOutputStream();
                //Output BOM
                outputStream.write(0xef);
                outputStream.write(0xbb);
                outputStream.write(0xbf);
                //Output fixed wording (CSV header)
                outputStream.write("Operation date,Operation time,User ID,User name,Operation details,Operation target person ID ・ ・ ・\n".getBytes());
                if (super.isImmediateFlush()) {
                    outputStream.flush();
                }
            } finally {
                lock.unlock();
            }
        }
    }
}

--Since I wanted to use the RollingFileAppender mechanism in Logback as it is for monthly log rotation, I will use a class that inherits RollingFileAppender. --For the output target (activeFile), the BOM and fixed wording (CSV header) are output only when the target file is empty. --In the Stack Overflow page above, the fixed wording was set externally, but in this implementation it is hard coded. --In the actual code, application-specific item names are set in the operation details, operation target person ID, and so on.

Creating a class for log output

I wanted to standardize the login user information acquisition process, and I also wanted to standardize Logger, so I decided to prepare a class.

AuthLogger.java


@Component
public class AuthLogger {

    /**Logger that outputs audit logs*/
    public static final Logger AUTH_LOGGER = LoggerFactory.getLogger(AuthLogger.class);

    /**Service dealing with individuals*/
    @Autowired
    protected PersonService personService;

    /**
     *The audit log is output without specifying the target individual.
     *
     * @param operation Operation details
     */
    public void log(String operation) {
        log(operation, null);
    }

    /**
     *Specify the target individual and output the audit log.
     *
     * @param operation Operation details
     * @param personId ID of the person who handled it
     */
    public void log(String operation, String personId) {
        SsoLoginData ssoLoginData = SsoLoginDataUtil.getSsoLoginData(); //Get login user information
        AUTH_LOGGER.trace(String.join(
            ",", //Output separated by commas
            //Operation date and operation time are logback.Not required here as it is set in the layout in xml
            ssoLoginData.getPersonId(), //User ID
            ssoLoginData.getUserName(), //User name
            operation, //Operation details
            personId //User name
            //Actually, set the output contents using personService here
        ));
    }
}

--Since it was an application built with SpringBoot, I decided to handle this class as a bean as well. That's why we've added Component. --PersonService is DI with @Autowired. --When generating Logger, I decided to pass AuthLogger.class as it is. --Therefore, in logback.xml, the fully qualified class name of AuthLogger is specified as name. --We have prepared both a method that specifies only the operation content and a method that specifies the ID of the target person who handled it. --In the process where the target person is not specified, the log is output by the former. --I'm using String.join to get the CSV. --If you want to enclose it in double quotes, or if the output value contains line breaks, we recommend using a CSV-specific library such as opencsv.

Added logback.xml settings

Add the following settings to logback.xml.

logback.xml


  <appender name="AUTH_APPENDER" class="your.project.web.common.util.AuthFileAppender">
    <file>/your/file/path.csv</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>/your/file/path.%d{yyyy-MM}.csv</fileNamePattern>
    </rollingPolicy>
    <encoder>
      <charset>UTF-8</charset>
      <pattern>%d{yyyy-MM-dd},%d{HH:mm:ss},%msg%n</pattern>
    </encoder>
  </appender>

  <logger name="your.project.web.common.util.AuthLogger" level="TRACE" additivity="false">
    <appender-ref ref="AUTH_APPENDER" />
  </logger>

--About appender settings --Use the AuthFileAppender mentioned above. --The part of / your / file / path is actually$ {log.auth.output}, and it is replaced with the value according to the environment running when the application is built. --The fileNamePattern of the rollingPolicy is % d {yyyy-MM} to rotate on a monthly basis. --I decided to use the Logback function to output the operation date and operation time, which are the first two items of CSV. Therefore, % d {yyyy-MM-dd},% d {HH: mm: ss}, is set at the beginning of the pattern. --About logger settings --name is the fully qualified class name of AuthLogger as described above. --If you specified a unique name in AuthLogger when generating Logger, you can specify that name here as well. --By specifying ʻadditivity = "false" `. Only AUTH_APPENDER is used.

Add log output to individual implementation

This time, I decided to DI the above-mentioned AuthLogger in the Controller of Spring MVC and execute the log method directly. (Implementation is omitted here.) Depending on the scale and requirements of the project, I think there is an option to output by AOP.

Summary and impressions

Logback didn't have a function to put a fixed wording (including BOM) at the beginning of the file, so I decided to prepare my own Appender, but it was relatively easy to realize. This time, it was assumed that the user would check the output log file directly in Excel, so we took the above measures. Depending on the requirements, you can output the log file without BOM / header and then combine it with the BOM / header with a script.

Recommended Posts

Log out a CSV file that can be read in Excel using logback
Create a jar file that can be executed in Gradle
Write a class that can be ordered in Java
Read items containing commas in a CSV file without splitting (Java)
How to make a key pair of ecdsa in a format that can be read by Java
I want to be able to read a file using refile with administrate [rails6]
Read a string in a PDF file with Java
A bat file that uses Java in windows
Basic functional interface that can be understood in 3 minutes
Convenient shortcut keys that can be used in Eclipse
List of devices that can be previewed in Swift UI
Create a page control that can be used with RecyclerView
Activate Excel file A1 cell of each sheet in Java
Introduction to Rakefile that can be done in about 10 minutes
Find a Switch statement that can be converted to a Switch expression
Java (super beginner edition) that can be understood in 180 seconds
Object-oriented design that can be used when you want to return a response in form format