src/core/net/sf/basedb/util/extensions/logging/ExtensionsLog.java

Code
Comments
Other
Rev Date Author Line
8118 22 Feb 23 nicklas 1 /**
8118 22 Feb 23 nicklas 2   $Id $
8118 22 Feb 23 nicklas 3
8118 22 Feb 23 nicklas 4   Copyright (C) 2023 Nicklas Nordborg
8118 22 Feb 23 nicklas 5
8118 22 Feb 23 nicklas 6   This file is part of BASE - BioArray Software Environment.
8118 22 Feb 23 nicklas 7   Available at http://base.thep.lu.se/
8118 22 Feb 23 nicklas 8
8118 22 Feb 23 nicklas 9   BASE is free software; you can redistribute it and/or
8118 22 Feb 23 nicklas 10   modify it under the terms of the GNU General Public License
8118 22 Feb 23 nicklas 11   as published by the Free Software Foundation; either version 3
8118 22 Feb 23 nicklas 12   of the License, or (at your option) any later version.
8118 22 Feb 23 nicklas 13
8118 22 Feb 23 nicklas 14   BASE is distributed in the hope that it will be useful,
8118 22 Feb 23 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
8118 22 Feb 23 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
8118 22 Feb 23 nicklas 17   GNU General Public License for more details.
8118 22 Feb 23 nicklas 18
8118 22 Feb 23 nicklas 19   You should have received a copy of the GNU General Public License
8118 22 Feb 23 nicklas 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
8118 22 Feb 23 nicklas 21 */
8124 09 Mar 23 nicklas 22 package net.sf.basedb.util.extensions.logging;
8118 22 Feb 23 nicklas 23
8119 06 Mar 23 nicklas 24 import java.util.Deque;
8118 22 Feb 23 nicklas 25 import java.util.HashMap;
8119 06 Mar 23 nicklas 26 import java.util.Iterator;
8118 22 Feb 23 nicklas 27 import java.util.Map;
8119 06 Mar 23 nicklas 28 import java.util.concurrent.ConcurrentLinkedDeque;
8120 07 Mar 23 nicklas 29 import java.util.concurrent.locks.Lock;
8120 07 Mar 23 nicklas 30 import java.util.concurrent.locks.ReadWriteLock;
8120 07 Mar 23 nicklas 31 import java.util.concurrent.locks.ReentrantReadWriteLock;
8118 22 Feb 23 nicklas 32
8119 06 Mar 23 nicklas 33 import org.apache.commons.collections4.iterators.UnmodifiableIterator;
8119 06 Mar 23 nicklas 34
8118 22 Feb 23 nicklas 35 /**
8118 22 Feb 23 nicklas 36   Instances hold the log entries for a single extension.
8118 22 Feb 23 nicklas 37
8118 22 Feb 23 nicklas 38   @author nicklas
8118 22 Feb 23 nicklas 39   @since 3.19.8
8118 22 Feb 23 nicklas 40 */
8125 09 Mar 23 nicklas 41 public class ExtensionsLog 
8118 22 Feb 23 nicklas 42 {
8125 09 Mar 23 nicklas 43   private static Map<String, ExtensionsLog> logs = new HashMap<>();
8118 22 Feb 23 nicklas 44   
8118 22 Feb 23 nicklas 45   /**
8118 22 Feb 23 nicklas 46     Get the service log for the given extension. If the log
8118 22 Feb 23 nicklas 47     doens't exists yet it can optionally be created.
8118 22 Feb 23 nicklas 48   */
8125 09 Mar 23 nicklas 49   public static ExtensionsLog getLog(String extensionId, boolean create)
8118 22 Feb 23 nicklas 50   {
8125 09 Mar 23 nicklas 51     ExtensionsLog log = logs.get(extensionId);
8118 22 Feb 23 nicklas 52     if (log == null && create)
8118 22 Feb 23 nicklas 53     {
8118 22 Feb 23 nicklas 54       synchronized (logs)
8118 22 Feb 23 nicklas 55       {
8118 22 Feb 23 nicklas 56         log = logs.get(extensionId);
8118 22 Feb 23 nicklas 57         if (log == null)
8118 22 Feb 23 nicklas 58         {
8125 09 Mar 23 nicklas 59           log = new ExtensionsLog(extensionId);
8118 22 Feb 23 nicklas 60           logs.put(extensionId, log);
8118 22 Feb 23 nicklas 61         }
8118 22 Feb 23 nicklas 62       }
8118 22 Feb 23 nicklas 63     }
8118 22 Feb 23 nicklas 64     return log;
8118 22 Feb 23 nicklas 65   }
8118 22 Feb 23 nicklas 66   
8118 22 Feb 23 nicklas 67   /**
8118 22 Feb 23 nicklas 68     Get a logger that can add entries to the log for the given extension.
8118 22 Feb 23 nicklas 69     If the log doesn't exists yet, it can optionally be created.
8118 22 Feb 23 nicklas 70   */
8125 09 Mar 23 nicklas 71   public static ExtensionsLogger getLogger(String extensionId, boolean create)
8118 22 Feb 23 nicklas 72   {
8125 09 Mar 23 nicklas 73     ExtensionsLog log = getLog(extensionId, create);
8118 22 Feb 23 nicklas 74     return log == null ? null : log.createLogger();
8118 22 Feb 23 nicklas 75   }
8127 09 Mar 23 nicklas 76
8127 09 Mar 23 nicklas 77   /**
8127 09 Mar 23 nicklas 78     Create a logger that can add entries to the log for the given extension.
8127 09 Mar 23 nicklas 79     If the log doesn't exists yet, it is be created.
8127 09 Mar 23 nicklas 80   */
8127 09 Mar 23 nicklas 81   public static ExtensionsLogger createLogger(String extensionId)
8127 09 Mar 23 nicklas 82   {
8127 09 Mar 23 nicklas 83     return getLog(extensionId, true).createLogger();
8127 09 Mar 23 nicklas 84   }
8127 09 Mar 23 nicklas 85
8118 22 Feb 23 nicklas 86   private final String extensionId;
8119 06 Mar 23 nicklas 87   private final Deque<LogEntry> logEntries;
8120 07 Mar 23 nicklas 88   private final ReadWriteLock lock;
8119 06 Mar 23 nicklas 89   
8119 06 Mar 23 nicklas 90   private int maxEntries;
8119 06 Mar 23 nicklas 91   private volatile int numEntries;
8119 06 Mar 23 nicklas 92   private volatile int numWarnings;
8119 06 Mar 23 nicklas 93   private volatile int numErrors;
8119 06 Mar 23 nicklas 94   
8118 22 Feb 23 nicklas 95   private boolean isDebugEnabled;
8123 09 Mar 23 nicklas 96   private boolean copyToStdout;
8118 22 Feb 23 nicklas 97   
8125 09 Mar 23 nicklas 98   private ExtensionsLog(String extensionId)
8118 22 Feb 23 nicklas 99   {
8118 22 Feb 23 nicklas 100     this.extensionId = extensionId;
8119 06 Mar 23 nicklas 101     this.logEntries = new ConcurrentLinkedDeque<>();
8119 06 Mar 23 nicklas 102     this.maxEntries = 200;
8120 07 Mar 23 nicklas 103     this.lock = new ReentrantReadWriteLock();
8118 22 Feb 23 nicklas 104   }
8118 22 Feb 23 nicklas 105   
8118 22 Feb 23 nicklas 106   /**
8118 22 Feb 23 nicklas 107     Get the extension id of the log.
8118 22 Feb 23 nicklas 108   */
8118 22 Feb 23 nicklas 109   public String getExtensionId()
8118 22 Feb 23 nicklas 110   {
8118 22 Feb 23 nicklas 111     return extensionId;
8118 22 Feb 23 nicklas 112   }
8118 22 Feb 23 nicklas 113   
8119 06 Mar 23 nicklas 114   /**
8119 06 Mar 23 nicklas 115     Is the debug-level enabled?
8119 06 Mar 23 nicklas 116   */
8118 22 Feb 23 nicklas 117   public boolean isDebugEnabled()
8118 22 Feb 23 nicklas 118   {
8118 22 Feb 23 nicklas 119     return isDebugEnabled;
8118 22 Feb 23 nicklas 120   }
8118 22 Feb 23 nicklas 121   
8118 22 Feb 23 nicklas 122   /**
8119 06 Mar 23 nicklas 123     Enable or disable debug-level logging.
8119 06 Mar 23 nicklas 124   */
8119 06 Mar 23 nicklas 125   public void setDebugEnabled(boolean debugEnabled)
8119 06 Mar 23 nicklas 126   {
8119 06 Mar 23 nicklas 127     this.isDebugEnabled = debugEnabled;
8119 06 Mar 23 nicklas 128   }
8119 06 Mar 23 nicklas 129   
8119 06 Mar 23 nicklas 130   /**
8123 09 Mar 23 nicklas 131     Are log entries copied to standard output?
8123 09 Mar 23 nicklas 132   */
8123 09 Mar 23 nicklas 133   public boolean isCopyingToStdout()
8123 09 Mar 23 nicklas 134   {
8123 09 Mar 23 nicklas 135     return copyToStdout;
8123 09 Mar 23 nicklas 136   }
8123 09 Mar 23 nicklas 137   
8123 09 Mar 23 nicklas 138   /**
8123 09 Mar 23 nicklas 139     Enable or disable copies of log entries to standard output.
8123 09 Mar 23 nicklas 140     When enabled, TRACE-level log entries are also written if
8123 09 Mar 23 nicklas 141     debugging is enabled.
8123 09 Mar 23 nicklas 142   */
8123 09 Mar 23 nicklas 143   public void setCopyToStdout(boolean copyToStdout)
8123 09 Mar 23 nicklas 144   {
8123 09 Mar 23 nicklas 145     this.copyToStdout = copyToStdout;
8123 09 Mar 23 nicklas 146   }
8123 09 Mar 23 nicklas 147   
8123 09 Mar 23 nicklas 148   /**
8119 06 Mar 23 nicklas 149     Get the max number of entries to keep in the log.
8119 06 Mar 23 nicklas 150   */
8119 06 Mar 23 nicklas 151   public int getMaxEntries()
8119 06 Mar 23 nicklas 152   {
8119 06 Mar 23 nicklas 153     return maxEntries;
8119 06 Mar 23 nicklas 154   }
8119 06 Mar 23 nicklas 155   
8119 06 Mar 23 nicklas 156   /**
8119 06 Mar 23 nicklas 157     Get the current number of entries in the log.
8119 06 Mar 23 nicklas 158   */
8119 06 Mar 23 nicklas 159   public int getNumEntries()
8119 06 Mar 23 nicklas 160   {
8119 06 Mar 23 nicklas 161     return numEntries;
8119 06 Mar 23 nicklas 162   }
8119 06 Mar 23 nicklas 163   
8119 06 Mar 23 nicklas 164   /**
8119 06 Mar 23 nicklas 165     Get the current number of warning entries in the log.
8119 06 Mar 23 nicklas 166   */
8119 06 Mar 23 nicklas 167   public int getNumWarnings()
8119 06 Mar 23 nicklas 168   {
8119 06 Mar 23 nicklas 169     return numWarnings;
8119 06 Mar 23 nicklas 170   }
8119 06 Mar 23 nicklas 171   
8119 06 Mar 23 nicklas 172   /**
8119 06 Mar 23 nicklas 173     Get the current number of error entries in the log.
8119 06 Mar 23 nicklas 174   */
8119 06 Mar 23 nicklas 175   public int getNumErrors()
8119 06 Mar 23 nicklas 176   {
8119 06 Mar 23 nicklas 177     return numErrors;
8119 06 Mar 23 nicklas 178   }
8119 06 Mar 23 nicklas 179   
8119 06 Mar 23 nicklas 180   /**
8120 07 Mar 23 nicklas 181     Clear the log.
8120 07 Mar 23 nicklas 182   */
8120 07 Mar 23 nicklas 183   public void clearLog()
8120 07 Mar 23 nicklas 184   {
8120 07 Mar 23 nicklas 185     // We need the write lock here since we are changing multiple properties
8120 07 Mar 23 nicklas 186     Lock l = lock.writeLock();
8120 07 Mar 23 nicklas 187     try
8120 07 Mar 23 nicklas 188     {
8120 07 Mar 23 nicklas 189       l.lock();
8120 07 Mar 23 nicklas 190       logEntries.clear();
8120 07 Mar 23 nicklas 191       numEntries = 0;
8120 07 Mar 23 nicklas 192       numWarnings = 0;
8120 07 Mar 23 nicklas 193       numErrors = 0;
8120 07 Mar 23 nicklas 194     }
8120 07 Mar 23 nicklas 195     finally
8120 07 Mar 23 nicklas 196     {
8120 07 Mar 23 nicklas 197       l.unlock();
8120 07 Mar 23 nicklas 198     }
8120 07 Mar 23 nicklas 199   }
8120 07 Mar 23 nicklas 200   
8120 07 Mar 23 nicklas 201   /**
8118 22 Feb 23 nicklas 202     Create a logger that adds messages to this log.
8118 22 Feb 23 nicklas 203   */
8125 09 Mar 23 nicklas 204   public ExtensionsLogger createLogger()
8118 22 Feb 23 nicklas 205   {
8125 09 Mar 23 nicklas 206     return new ExtensionsLogger(this);
8118 22 Feb 23 nicklas 207   }
8118 22 Feb 23 nicklas 208
8119 06 Mar 23 nicklas 209   /**
8119 06 Mar 23 nicklas 210     Add a log entry.
8119 06 Mar 23 nicklas 211     DEBUG level messages are skipped unless debug-level is enabled.
8123 09 Mar 23 nicklas 212     TRACE level messages are always skipped but can be written to
8123 09 Mar 23 nicklas 213     standard output if that option and debugging is enabled.
8119 06 Mar 23 nicklas 214   */
8118 22 Feb 23 nicklas 215   void add(LogEntry entry)
8118 22 Feb 23 nicklas 216   {
8125 09 Mar 23 nicklas 217     if (copyToStdout && (entry.getLevel() != LogLevel.TRACE || isDebugEnabled))
8123 09 Mar 23 nicklas 218     {
8123 09 Mar 23 nicklas 219       System.out.println("["+extensionId+"] "+entry);
8123 09 Mar 23 nicklas 220     }
8125 09 Mar 23 nicklas 221     if (entry.getLevel() == LogLevel.TRACE || (entry.getLevel() == LogLevel.DEBUG && !isDebugEnabled)) 
8123 09 Mar 23 nicklas 222     {
8123 09 Mar 23 nicklas 223       return;
8123 09 Mar 23 nicklas 224     }
8120 07 Mar 23 nicklas 225     // A read lock should be enough since we are using a ConcurrentLinkedDeque that supports multiple threads
8120 07 Mar 23 nicklas 226     Lock l = lock.readLock();
8120 07 Mar 23 nicklas 227     try
8118 22 Feb 23 nicklas 228     {
8120 07 Mar 23 nicklas 229       l.lock();
8120 07 Mar 23 nicklas 230       logEntries.addFirst(entry);
8125 09 Mar 23 nicklas 231       if (entry.getLevel() == LogLevel.WARNING) numWarnings++;
8125 09 Mar 23 nicklas 232       if (entry.getLevel() == LogLevel.ERROR) numErrors++;
8123 09 Mar 23 nicklas 233       
8129 14 Mar 23 nicklas 234       if (++numEntries > maxEntries)
8120 07 Mar 23 nicklas 235       {
8120 07 Mar 23 nicklas 236         // Remove one entry
8120 07 Mar 23 nicklas 237         numEntries--;
8120 07 Mar 23 nicklas 238         LogEntry last = logEntries.removeLast();
8125 09 Mar 23 nicklas 239         if (last.getLevel() == LogLevel.WARNING) numWarnings--;
8125 09 Mar 23 nicklas 240         if (last.getLevel() == LogLevel.ERROR) numErrors--;
8120 07 Mar 23 nicklas 241       }
8118 22 Feb 23 nicklas 242     }
8120 07 Mar 23 nicklas 243     finally
8120 07 Mar 23 nicklas 244     {
8120 07 Mar 23 nicklas 245       l.unlock();
8120 07 Mar 23 nicklas 246     }
8118 22 Feb 23 nicklas 247   }
8118 22 Feb 23 nicklas 248   
8118 22 Feb 23 nicklas 249   /**
8119 06 Mar 23 nicklas 250     Get an iterator that is returning all entries in the log.
8119 06 Mar 23 nicklas 251     The returned iterator is read-only and the {@link Iterator#remove()} 
8119 06 Mar 23 nicklas 252     method will throw an exception.
8119 06 Mar 23 nicklas 253   */
8119 06 Mar 23 nicklas 254   public Iterator<LogEntry> getLogEntries()
8119 06 Mar 23 nicklas 255   {
8119 06 Mar 23 nicklas 256     return UnmodifiableIterator.unmodifiableIterator(logEntries.iterator());
8119 06 Mar 23 nicklas 257   }
8123 09 Mar 23 nicklas 258
8118 22 Feb 23 nicklas 259 }