Java tutorial
/** * Copyright 2011, Deft Labs. * * 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 com.deftlabs.logging.mongo; // Mongo import com.mongodb.DB; import com.mongodb.Mongo; import com.mongodb.MongoURI; import com.mongodb.MongoOptions; import com.mongodb.MongoException; import com.mongodb.DBCollection; import com.mongodb.BasicDBObject; // Java import java.util.logging.Level; import java.util.logging.Filter; import java.util.logging.LogRecord; import java.util.logging.LogManager; import java.util.logging.Handler; import java.util.logging.ErrorManager; import java.text.MessageFormat; import java.net.InetAddress; import java.net.UnknownHostException; import java.lang.management.ManagementFactory; import java.util.concurrent.LinkedBlockingQueue; import java.util.LinkedList; import java.io.Writer; import java.io.StringWriter; import java.io.PrintWriter; /** * The Mongo logger for * <a href="http://en.wikipedia.org/wiki/Java_logging_framework">Java Logging</a>. * * <p>Most of this documentation came from the Java API docs.</p> * * <p> * <b>Configuration:</b> By default each MongoHandler is initialized using the * following LogManager configuration properties. If properties are not defined * (or have invalid values) then the specified default values are used. * </p> * <pre> * com.deftlabs.logging.mongo.MongoHandler.level specifies the default level for the Handler (defaults to Level.ALL). * com.deftlabs.logging.mongo.MongoHandler.filter specifies the name of a Filter class to use (defaults to no Filter). * com.deftlabs.logging.mongo.MongoHandler.encoding the name of the character set encoding to use (else platform default). * com.deftlabs.logging.mongo.MongoHandler.mongoUri The connection uri with args - see: http://api.mongodb.org/java/current/com/mongodb/MongoURI.html * com.deftlabs.logging.mongo.MongoHandler.databaseName The name of the Mongo database (defaults to mongo-java-logging). * com.deftlabs.logging.mongo.MongoHandler.collectionName The name of the Mongo collection (defaults to log). * com.deftlabs.logging.mongo.MongoHandler.nodeName The optional name of the node. Otherwise, uses ip address(s). * com.deftlabs.logging.mongo.MongoHandler.maxQueueSize Set the max queue size. If the queue is full, new messages * are dropped. This was done to avoid memory issues and application blocking (defualt is 500). * com.deftlabs.logging.mongo.MongoHandler.closeSleepTime The optional (default is 500 ms) amount to sleep before stopping. This * allows log messsages in the queue/buffer a chance to be persisted to Mongo. This value is in milliseconds. * com.deftlabs.logging.mongo.MongoHandler.writerThreadCount The optional config param to increase writer threads (default is 1). * * * TODO: Configure rolling, time zone, etc? * </pre> * */ public class MongoHandler extends Handler { public MongoHandler() throws Exception { super(); configure(); } public void publish(final LogRecord pRcd) { if (!isLoggable(pRcd)) return; try { if (_queue == null) configure(); final BasicDBObject msg = new BasicDBObject(); msg.put(LogMsg.LEVEL.field, pRcd.getLevel().getName()); final Object[] params = pRcd.getParameters(); if (params == null) msg.put(LogMsg.MSG.field, pRcd.getMessage()); else msg.put(LogMsg.MSG.field, MessageFormat.format(pRcd.getMessage(), params)); msg.put(LogMsg.LOGGER.field, pRcd.getLoggerName()); msg.put(LogMsg.MSG_SEQ.field, pRcd.getSequenceNumber()); msg.put(LogMsg.THREAD.field, pRcd.getThreadID()); msg.put(LogMsg.RES_BUNDLE.field, pRcd.getResourceBundleName()); msg.put(LogMsg.TIMESTAMP.field, pRcd.getMillis()); msg.put(LogMsg.SRC_METHOD.field, pRcd.getSourceMethodName()); msg.put(LogMsg.SRC_CLASS.field, pRcd.getSourceClassName()); msg.put(LogMsg.THROWN.field, throwableToString(pRcd.getThrown())); msg.put(LogMsg.APP_PID.field, _pid); msg.put(LogMsg.NODE_NAME.field, _nodeName); if (!_queue.offer(msg)) { getErrorManager().error("Queue is full", null, ErrorManager.WRITE_FAILURE); } } catch (final Exception e) { getErrorManager().error(e.getMessage(), e, ErrorManager.WRITE_FAILURE); } } /** * Serialize the throwable to a string. If T is null, null is returned. */ private static String throwableToString(final Throwable t) { if (t == null) return null; final Writer result = new StringWriter(); final PrintWriter printWriter = new PrintWriter(result); t.printStackTrace(printWriter); return result.toString(); } /** * Send the message to mongo (i.e., insert in the collection). */ private void sendToMongo(final BasicDBObject pMsg) { try { getCollection().insert(pMsg); } catch (final Exception me) { getErrorManager().error(me.getMessage(), me, ErrorManager.WRITE_FAILURE); } } /** * Returns the configured collection. */ private DBCollection getCollection() throws UnknownHostException { if (_collection != null) return _collection; synchronized (sMutex) { if (_collection != null) return _collection; _mongo = new Mongo(new MongoURI(_mongoUri)); final DB db = _mongo.getDB(_databaseName); _collection = _mongo.getDB(_databaseName).getCollection(_collectionName); return _collection; } } public void flush() { } /** * Stop the thread. */ public void close() { if (_closeSleepTime != null && _closeSleepTime > 0) { try { Thread.sleep(_closeSleepTime); } catch (final InterruptedException ie) { } } if (_msgWriterThreads == null) return; try { for (MsgWriterThread t : _msgWriterThreads) t.interrupt(); } catch (final Throwable t) { } } private void configure() throws Exception { final LogManager manager = LogManager.getLogManager(); final String clazz = getClass().getName(); final String pid = ManagementFactory.getRuntimeMXBean().getName(); _pid = pid.substring(0, pid.indexOf("@")); _nodeName = getStrProp(clazz + ".nodeName", null); if (_nodeName == null) { _nodeName = InetAddress.getLocalHost().getHostName(); } _mongoUri = getStrProp(clazz + ".mongoUri", "mongodb://127.0.0.1:27017"); _databaseName = getStrProp(clazz + ".databaseName", "mongo-java-logging"); _collectionName = getStrProp(clazz + ".collectionName", "log"); _closeSleepTime = getIntProp(clazz + ".closeSleepTime", 500); final int writerThreadCount = getIntProp(clazz + ".writerThreadCount", 1); setLevel(getLevelProp(clazz + ".level", Level.ALL)); final int maxQueueSize = getIntProp(clazz + ".maxQueueSize", 500); if (_queue == null) { _queue = new LinkedBlockingQueue<BasicDBObject>(maxQueueSize); } if (_msgWriterThreads == null) { _msgWriterThreads = new MsgWriterThread[writerThreadCount]; for (int idx = 0; idx < writerThreadCount; idx++) { final MsgWriterThread msgWriterThread = new MsgWriterThread(); msgWriterThread.start(); msgWriterThread.setName("mongo-java-logging-" + idx); _msgWriterThreads[idx] = msgWriterThread; } } setFilter(getFilterProp(clazz + ".filter", null)); try { setEncoding(getStrProp(clazz + ".encoding", null)); } catch (final Exception e) { try { setEncoding(null); } catch (final Exception e1) { } } } private Filter getFilterProp(final String pName, final Filter pDefault) { final String v = LogManager.getLogManager().getProperty(pName); try { return (v != null) ? (Filter) ClassLoader.getSystemClassLoader().loadClass(v).newInstance() : pDefault; } catch (final Exception e) { } return pDefault; } private Level getLevelProp(final String pName, final Level pDefault) { final String v = LogManager.getLogManager().getProperty(pName); if (v == null) return pDefault; try { return Level.parse(v.trim()); } catch (final Exception e) { return pDefault; } } private String getStrProp(final String pName, final String pDefault) { String v = LogManager.getLogManager().getProperty(pName); return (v == null) ? pDefault : v.trim(); } private boolean getBoolProp(final String pName, final boolean pDefault) { String v = LogManager.getLogManager().getProperty(pName); if (v == null) return pDefault; v = v.toLowerCase(); if (v.equals("true") || v.equals("1")) return true; else if (v.equals("false") || v.equals("0")) return false; return pDefault; } private int getIntProp(final String pName, final int pDefault) { final String v = LogManager.getLogManager().getProperty(pName); if (v == null) return pDefault; try { return Integer.parseInt(v.trim()); } catch (final Exception e) { return pDefault; } } private Mongo _mongo; private volatile DBCollection _collection; // Config private String _mongoUri; private String _databaseName; private String _collectionName; private String _pid; private String _nodeName; private Integer _closeSleepTime; private static final Object sMutex = new Object(); private LinkedBlockingQueue<BasicDBObject> _queue; private MsgWriterThread[] _msgWriterThreads; /** * Read the messages from the queue and write to mongo. */ private class MsgWriterThread extends Thread { public void run() { while (true) { try { sendToMongo(_queue.take()); } catch (final InterruptedException ie) { break; } catch (final Throwable t) { if (t instanceof Exception) { getErrorManager().error(t.getMessage(), (Exception) t, ErrorManager.WRITE_FAILURE); } else { getErrorManager().error(t.getMessage(), null, ErrorManager.WRITE_FAILURE); } } } } } }