/* * Copyright (C) 2011-2014 Laurent CLOUET * Author Laurent CLOUET * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package com.sheepit.client; import lombok.Getter; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; public final class Log { private final String LEVEL_DEBUG = "debug"; private final String LEVEL_INFO = "info"; private final String LEVEL_ERROR = "error"; public final int MAX_SIZE = 10000000; // in B public final int PERIOD = 3600000; // duration in ms between two shrink @Getter private static Log instance = null; private Map> checkpoints = new HashMap<>(); private int lastCheckPoint; private DateFormat dateFormat; private boolean printStdOut; private boolean debugLevel; private String logFile; private long lastShrink; private Log(boolean debugLevel_, boolean print_, String logDirectory_) { this.debugLevel = debugLevel_; this.printStdOut = print_; this.logFile = logDirectory_ + File.separator + "sheepit.log"; this.lastCheckPoint = 0; this.checkpoints.put(this.lastCheckPoint, new ArrayList()); this.dateFormat = new SimpleDateFormat("dd-MM HH:mm:ss"); this.lastShrink = 0; } public void debug(String msg_) { this.debug(-1, msg_); } public void debug(int point_, String msg_) { this.append(point_, LEVEL_DEBUG, msg_); } public void info(String msg_) { this.info(-1, msg_); } public void info(int point_, String msg_) { this.append(point_, LEVEL_INFO, msg_); } public void error(String msg_) { this.error(-1, msg_); } public void error(int point_, String msg_) { this.append(point_, LEVEL_ERROR, msg_); } private synchronized void append(int point_, String level_, String msg_) { try { int checkpointToWrite = (point_ > 0 ? point_ : this.lastCheckPoint); if ("".equals(msg_) == false) { String line = this.dateFormat.format(new Date()) + " (" + level_ + ") " + msg_; // always add to memory if (this.checkpoints.containsKey(checkpointToWrite) && this.checkpoints.get(checkpointToWrite) != null) { this.checkpoints.get(checkpointToWrite).add(line); } // DEBUG mode for not always use stdout/file if (LEVEL_DEBUG.equals(level_) && this.debugLevel == false) { return; } if (this.printStdOut) { System.out.println(line); } if (this.logFile != null && this.logFile.isEmpty() == false) { try { Files.write(Paths.get(this.logFile), (line + "\n").getBytes(), StandardOpenOption.APPEND, StandardOpenOption.CREATE); } catch (IOException e) { // :( catch-22, we can't really write the exception on the log file } } } } catch (Exception e) { // Nothing to do here. Just allow the thread to continue } } public int newCheckPoint() { int time = (int) (new Date().getTime()); this.checkpoints.put(time, new ArrayList()); this.lastCheckPoint = time; return this.lastCheckPoint; } public Optional> getForCheckPoint(int point_) { return Optional.ofNullable(this.checkpoints.get(point_)); } public void removeCheckPoint(int point_) { try { this.checkpoints.remove(point_); } catch (UnsupportedOperationException e) { } } public synchronized void shrink() { if (System.currentTimeMillis() > (this.lastShrink + this.PERIOD)) { try (RandomAccessFile raf = new RandomAccessFile(this.logFile, "r")) { long fileLength = raf.length(); if (fileLength < MAX_SIZE) { // nothing to do, it's a small file return; } long startPosition = Math.max(0, fileLength - MAX_SIZE); raf.seek(startPosition); byte[] newBytes = new byte[(int) (fileLength - startPosition)]; raf.readFully(newBytes); Path inputPath = Paths.get(this.logFile); Files.write(inputPath, newBytes); } catch (IOException e) { e.printStackTrace(); } this.lastShrink = System.currentTimeMillis(); } } public static synchronized void setInstance(Configuration config) { String path = null; boolean print = false; boolean debug = false; if (config != null) { debug = config.isDebugLevel(); print = config.isPrintLog(); path = config.getLogDirectory() == null ? null : config.getLogDirectory(); } instance = new Log(debug, print, path); } public static synchronized void printCheckPoint(int point_) { Log log = getInstance(); Optional> logs = log.getForCheckPoint(point_); if (logs.isPresent()) { for (String alog : logs.get()) { System.out.println(alog); } } } }