org.hillview.utils.HillviewLogger.java Source code

Java tutorial

Introduction

Here is the source code for org.hillview.utils.HillviewLogger.java

Source

/*
 * Copyright (c) 2017 VMware Inc. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed 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.hillview.utils;

import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.internal.logging.JdkLoggerFactory;
import org.apache.commons.lang.StringEscapeUtils;

import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.*;

/**
 * Core API for logging in hillview.
 * The hillview logging keeps each log message on a single line.
 * It uses commas to separate the fields.
 * The result produced by format and arguments is escaped: quotes and newlines are escaped.
 * This is a typical log message format
2017-10-12 02:18:29.084,worker,INFO,ubuntu,pool-1-thread-1,org.hillview.maps.FindCsvFileMapper,apply,Find files in folder,/hillview/data
_______date_______________who__level_machine___thread___________class__________________________method__message______________args_________
 */
public class HillviewLogger {
    // Actual logging channel.
    private final Logger logger;
    // Machine where the core is running.
    private final String machine;
    // Role of machine (worker or web server).
    private final String role;
    // Default logger if users forget to initialize
    public static HillviewLogger instance = new HillviewLogger("none", null);

    private static final Logger GRPC_LOGGER;
    private static final Logger NETTY_LOGGER;

    static {
        InternalLoggerFactory.setDefaultFactory(JdkLoggerFactory.INSTANCE);
        GRPC_LOGGER = Logger.getLogger("io.grpc");
        GRPC_LOGGER.setLevel(Level.WARNING);
        NETTY_LOGGER = Logger.getLogger("io.grpc.netty.NettyServerHandler");
        NETTY_LOGGER.setLevel(Level.WARNING);
    }

    /**
     * Create a Hillview logger.
     * @param role      Who is doing the logging: web server, worker, test, etc.
     * @param filename  File where logs are to be written.  If null logs will be written to the
     *                  console.
     */
    private HillviewLogger(String role, @Nullable String filename) {
        // Disable all default logging
        LogManager.getLogManager().reset();
        this.logger = Logger.getLogger("Hillview");
        this.machine = this.checkCommas(Utilities.getHostName());
        this.role = this.checkCommas(role);
        this.logger.setLevel(Level.INFO);

        Formatter form = new SimpleFormatter() {
            final String[] components = new String[5];
            final String newline = System.lineSeparator();
            private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");

            @Override
            public synchronized String format(LogRecord record) {
                this.components[0] = HillviewLogger.this.checkCommas(df.format(new Date(record.getMillis())));
                this.components[1] = HillviewLogger.this.role;
                this.components[2] = HillviewLogger.this.checkCommas(record.getLevel().toString());
                this.components[3] = HillviewLogger.this.machine;
                this.components[4] = record.getMessage();
                String result = String.join(",", components);
                return result + this.newline;
            }
        };

        Handler handler;
        if (filename != null) {
            try {
                handler = new FileHandler(filename);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            handler = new ConsoleHandler();
        }
        handler.setFormatter(form);
        logger.addHandler(handler);
        File currentDirectory = new File(new File(".").getAbsolutePath());
        this.info("Starting logger", "Working directory: {0}", currentDirectory);
    }

    public static void initialize(String role, @Nullable String filename) {
        instance = new HillviewLogger(role, filename);
    }

    public void setLogLevel(final Level level) {
        this.logger.setLevel(level);
    }

    @SuppressWarnings("unused")
    public void shutdown() {
        Handler[] hs = this.logger.getHandlers();
        for (Handler h : hs)
            h.close();
    }

    public void info(String message, String format, Object... arguments) {
        if (!this.logger.isLoggable(Level.INFO))
            return;
        String text = this.createMessage(message, format, arguments);
        this.logger.info(text);
    }

    public void warn(String message, String format, Object... arguments) {
        if (!this.logger.isLoggable(Level.WARNING))
            return;
        String text = this.createMessage(message, format, arguments);
        this.logger.warning(text);
    }

    private void debug(String message, String format, Object... arguments) {
        if (!this.logger.isLoggable(Level.INFO))
            return;
        String text = this.createMessage(message, format, arguments);
        this.logger.log(Level.INFO, text);
    }

    public void error(String message, Throwable ex) {
        String text = this.createMessage(message, "{0}", Utilities.throwableToString(ex));
        this.logger.severe(text);
    }

    public void error(String message, String format, Object... arguments) {
        String text = this.createMessage(message, format, arguments);
        this.logger.severe(text);
    }

    private String checkCommas(String str) {
        if (str.contains(",")) {
            HillviewLogger.instance.error("Log message should contain no commas", "{0}", str);
            str = str.replace(",", ";");
        }
        return str;
    }

    private String createMessage(String message, String format, Object... arguments) {
        message = this.checkCommas(message);
        String text = MessageFormat.format(format, arguments);
        Thread current = Thread.currentThread();
        StackTraceElement[] stackTraceElements = current.getStackTrace();
        StackTraceElement caller = stackTraceElements[3];
        String quoted = this.quote(text);
        return String.join(",", current.getName(), caller.getClassName(), caller.getMethodName(), message, quoted);
    }

    private String quote(String message) {
        message = message.replace("\n", "\\n");
        return StringEscapeUtils.escapeCsv(message);
    }

    public void info(String message) {
        this.info(message, "");
    }

    public void warn(String message) {
        this.warn(message, "");
    }

    public void debug(String message) {
        this.debug(message, "");
    }

    public void error(String message) {
        this.error(message, "");
    }
}