/* * #%L * OW2 Chameleon - Fuchsia Framework * %% * Copyright (C) 2009 - 2014 OW2 Chameleon * %% * 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. * #L% */ /* Calimero - A library for KNX network access Copyright (C) 2006-2008 B. Malinowsky 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; either version 2 of the License, or at your option any later version. 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. Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ package tuwien.auto.calimero.log; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; /** * A LogWriter using a file resource as output destination for log information. * <p> * A file name is supplied on creation of this log writer, the file is opened and used for * further logging. After {@link #close()}ing the log writer, it cannot be opened * anymore.<br> * For output the platform's default character set is used.<br> * A maximum allowed file size may be specified to prevent file size explosion. If the * size limit is reached, the file content is deleted before any new output. * * @author B. Malinowsky */ public class LogFileWriter extends LogStreamWriter { // we do a distinction of the used default character set // for easier calculation of file size private static final byte byteEnc; private static final byte lineSep; static { // get line separator size lineSep = (byte) System.getProperty("line.separator").length(); // get character encoding byte size byte[] buf = new String(new char[] { ' ', ' ' }).getBytes(); // if length is 4 or 6 (with byte order mark) we have UTF-16 if (buf.length == 4 || buf.length == 6) byteEnc = 2; else { // check for UTF-8 with a character encoded in more bytes buf = new String(new char[] { '\uFFFF' }).getBytes(); // on length == 1 we have single byte encoding if (buf.length == 1) byteEnc = 1; else byteEnc = 3; } } private String file; // log file size in bytes private int logSize; // maximum allowed log file size in bytes private int maxSize; /** * Creates a LogFileWriter to write to the output file named by <code>file</code> * and open the file according to <code>append</code>. * <p> * * @param file file name in the file system to open or create, the path to the file * has to exist * @param append set this true to append output at end of file, or false to start * writing into an empty file at the beginning * @throws KNXLogException if path to file does not exist, if file can not be created * or opened */ public LogFileWriter(String file, boolean append) throws KNXLogException { if (append) { // if file does not exist, we will fail later anyway... final File f = new File(file); logSize = (int) f.length(); } this.file = file; formatOutput = false; try { createWriter(new FileOutputStream(file, append)); } catch (final FileNotFoundException e) { throw new KNXLogException(e.getMessage()); } catch (final SecurityException e) { throw new KNXLogException(e.getMessage()); } } /** * Like {@link #LogFileWriter(String, boolean)}, with the option to adjust the * automatic flush behavior of data. * <p> * * @param file file name in the file system to open or create * @param append set this true to append output at end of file, or false to start * writing into an empty file at the beginning * @param autoFlush set true to force data be immediately written to file after every * write() call, set false to buffer data and flush only on full buffer * @throws KNXLogException if file can not be created or opened */ public LogFileWriter(String file, boolean append, boolean autoFlush) throws KNXLogException { this(file, append); this.autoFlush = autoFlush; } /** * Like {@link #LogFileWriter(String, boolean)}, with the option to adjust the filter * log level for information logged by LogFileWriter. * <p> * * @param level log level used by this LogWriter to filter log information * @param file file name in the file system to open or create * @param append set this true to append output at end of file, or false to start * writing into an empty file at the beginning * @throws KNXLogException if file can not be created or opened */ public LogFileWriter(LogLevel level, String file, boolean append) throws KNXLogException { this(file, append); setLogLevel(level); } /** * Like {@link #LogFileWriter(LogLevel, String, boolean)}, with the option to specify * the maximum file size allowed for all output written. * <p> * During opening of <code>file</code>, the file size is checked to be smaller than * maxSize, otherwise the file content is erased.<br> * If a call to write() would exceed the maximum file size specified, the file content * is erased before the new log information is written. * * @param level log level used by this LogWriter to filter log information * @param file file name in the file system to open or create * @param append set this true to append output at end of file, or false to start * writing into an empty file at the beginning * @param maxSize maximum file size generated by this LogFileWriter * @throws KNXLogException if file can not be created or opened */ public LogFileWriter(LogLevel level, String file, boolean append, int maxSize) throws KNXLogException { this(level, file, append); setMaxSize(maxSize); ensureMaxSize(""); } /** * Like {@link #LogFileWriter(LogLevel, String, boolean, int)}, with the option to * adjust the automatic flush behavior of data. * <p> * * @param level log level used by this LogWriter to filter log information * @param file file name in the file system to open or create * @param append set this true to append output at end of file, or false to start * writing into an empty file at the beginning * @param maxSize maximum file size generated by this LogFileWriter * @param autoFlush set true to force data be immediately written to file after every * write() call, set false to buffer data and flush only on full buffer * @throws KNXLogException if file can not be created or opened */ public LogFileWriter(LogLevel level, String file, boolean append, int maxSize, boolean autoFlush) throws KNXLogException { this(level, file, append, maxSize); this.autoFlush = autoFlush; } /** * Returns the file name of the file resource used by this LogFileWriter or "" if the * log writer has already been closed. * <p> * The file name is the same as supplied on creation of this LogWriter, no path * resolving etc. was done. * * @return file name */ public final String getFileName() { return file; } /** * Sets the maximum allowed file size generated by this LogFileWriter. * <p> * The value is only set if <code>size</code> >= 0.<br> * If <code>size</code> has a value of 0, no file size limit is enforced. * * @param size new allowed file size in bytes */ public final void setMaxSize(int size) { if (size >= 0) maxSize = size; } /** * Returns the maximum allowed file size generated by this LogFileWriter, or 0 if no * maximum was set. * <p> * * @return maximum file size in bytes */ public final int getMaxSize() { return maxSize; } /* (non-Javadoc) * @see tuwien.auto.calimero.log.LogStreamWriter#write * (java.lang.String, tuwien.auto.calimero.log.LogLevel, java.lang.String) */ public void write(String logService, LogLevel level, String msg) { doFileWrite(logService, level, msg, null); } /* (non-Javadoc) * @see tuwien.auto.calimero.log.LogStreamWriter#write * (java.lang.String, tuwien.auto.calimero.log.LogLevel, java.lang.String, * java.lang.Throwable) */ public void write(String logService, LogLevel level, String msg, Throwable t) { doFileWrite(logService, level, msg, t); } /* (non-Javadoc) * @see tuwien.auto.calimero.log.LogStreamWriter#close() */ public void close() { super.close(); file = ""; } private void doFileWrite(String logService, LogLevel level, String msg, Throwable t) { if (logAllowed(level)) { final String s = formatOutput(logService, level, msg, t); ensureMaxSize(s); synchronized (this) { super.write(logService, level, s); logSize += getByteLength(s) + lineSep; } } } private void ensureMaxSize(String msg) { if (maxSize == 0) return; synchronized (this) { if (logSize + getByteLength(msg) + lineSep > maxSize) { // remember used file name final String fileName = file; close(); try { // no append, to reset file length to 0 createWriter(new FileOutputStream(fileName)); file = fileName; logSize = 0; } catch (final FileNotFoundException e) { getErrorHandler().error(this, "on creation of file " + fileName, e); } catch (final SecurityException e) { getErrorHandler().error(this, "access denied", e); } } } } private int getByteLength(String s) { if (byteEnc == 1) return s.length(); if (byteEnc == 2) return s.length() * 2; return s.getBytes().length; } }