The version of Apache log4j used by SoundHelix.

[[ 🗃 ^aEp6o apache-log4j ]] :: [📥 Inbox] [📤 Outbox] [🐤 Followers] [🤝 Collaborators] [🛠 Commits]

Clone

HTTPS: git clone https://vervis.peers.community/repos/aEp6o

SSH: git clone USERNAME@vervis.peers.community:aEp6o

Branches

Tags

PRE_UGLI_MOVE :: src / java / org / apache / log4j / db /

CustomSQLDBReceiver.java

/*
 * Copyright 1999,2004 The Apache Software Foundation.
 * 
 * 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.apache.log4j.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Hashtable;
import java.util.StringTokenizer;

import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.plugins.Pauseable;
import org.apache.log4j.plugins.Receiver;
import org.apache.log4j.scheduler.Job;
import org.apache.log4j.scheduler.Scheduler;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;
import org.apache.log4j.spi.location.LocationInfo;

/**
 * Converts log data stored in a database into LoggingEvents.
 * <p>
 * <b>NOTE:</b> This receiver cannot yet be created through Chainsaw's receiver panel.  
 * It must be created through an XML configuration file.
 * <p>
 * This receiver supports database configuration via ConnectionSource, in the
 * org.apache.log4j.db package: DriverManagerConnectionSource,
 * DataSourceConnectionSource, JNDIConnectionSource
 * <p>
 * This database receiver differs from DBReceiver in that this receiver relies
 * on custom SQL to retrieve logging event data, where DBReceiver requires the
 * use of a log4j-defined schema.
 * <p>
 * A 'refreshMillis' int parameter controls SQL execution. If 'refreshMillis' is
 * zero (the default), the receiver will run only one time. If it is set to any
 * other numeric value, the SQL will be executed on a recurring basis every
 * 'refreshMillis' milliseconds.
 * <p>
 * The receiver closes the connection and acquires a new connection on each 
 * execution of the SQL (use pooled connections if possible).
 * <p>
 * If the SQL will be executing on a recurring basis, specify the IDField param -
 * the column name holding the unique identifier (int) representing the logging
 * event.
 * <p>
 * As events are retrieved, the column represented by IDField is examined and
 * the largest value is held and used by the next execution of the SQL statement
 * to avoid retrieving previously processed events.
 * <p>
 * As an example, the IDField references a 'COUNTER' (int, auto-increment,
 * unique) column. The first execution of the SQL statement returns 500 rows,
 * with a final value in the COUNTER field of 500.
 * <p>
 * The SQL statement is manipulated prior to the next execution, adding ' WHERE
 * COUNTER > 500' to the statement to avoid retrieval of previously processed
 * events.
 * <p>
 * The select statement must provide ALL fields which define a LoggingEvent.
 * <p>
 * The SQL statement MUST include the columns: LOGGER, TIMESTAMP, LEVEL, THREAD,
 * MESSAGE, NDC, MDC, CLASS, METHOD, FILE, LINE, PROPERTIES, EXCEPTION
 * <p>
 * Use ' AS ' in the SQL statement to alias the SQL's column names to match your
 * database schema. (see example below).
 * <p>
 * Include all fields in the SQL statement, even if you don't have data for the
 * field (specify an empty string as the value for columns which you don't have
 * data).
 * <p>
 * The TIMESTAMP column must be a datetime.
 * <p>
 * Both a PROPERTIES column and an MDC column are supported. These fields
 * represent Maps on the logging event, but require the use of string
 * concatenation database functions to hold the (possibly multiple) name/value
 * pairs in the column.
 * <p>
 * For example, to include both 'userid' and 'lastname' properties in the
 * logging event (from either the PROPERTIES or MDC columns), the name/value
 * pairs must be concatenated together by your database.
 * <p>
 * The resulting PROPERTIES or MDC column must have data in this format: {{name,
 * value, name2, value2}}
 * <p>
 * The resulting PROPERTIES column would contain this text: {{userid, someone,
 * lastname, mylastname}}
 * <p>
 * Here is an example of concatenating a PROPERTIES or MDC column using MySQL's
 * concat function, where the 'application' and 'hostname' parameters were fixed
 * text, but the 'log4jid' key's value is the value of the COUNTER column:
 * <p>
 * concat("{{application,databaselogs,hostname,mymachine,log4jid,", COUNTER,
 * "}}") as PROPERTIES
 * <p>
 * log4jid is a special property that is used by Chainsaw to represent an 'ID'
 * field. Specify this property to ensure you can map events in Chainsaw to
 * events in the database if you need to go back and view events at a later time
 * or save the events to XML for later analysis.
 * <p>
 * Here is a complete MySQL SQL statement which can be used to provide events to
 * Chainsaw:
 * <p>
 * select logger as LOGGER, timestamp as TIMESTAMP, level as LEVEL, thread as
 * THREAD, message as MESSAGE, ndc as NDC, mdc as MDC, class as CLASS, method as
 * METHOD, file as FILE, line as LINE,
 * concat("{{application,databaselogs,hostname,mymachine, log4jid,",
 * COUNTER,"}}") as PROPERTIES, "" as EXCEPTION from logtable
 * <p>
 * @author Scott Deboy <sdeboy@apache.org>
 * <p>
 */
public class CustomSQLDBReceiver extends Receiver implements Pauseable {

    protected volatile Connection connection = null;

    protected String sqlStatement = "";

    /**
     * By default we refresh data every 1000 milliseconds.
     * 
     * @see #setRefreshMillis
     */
    static int DEFAULT_REFRESH_MILLIS = 1000;

    int refreshMillis = DEFAULT_REFRESH_MILLIS;

    protected String idField = null;

    int lastID = -1;

    private static final String WHERE_CLAUSE = " WHERE ";

    private static final String AND_CLAUSE = " AND ";

    private boolean whereExists = false;

    private boolean paused = false;

    private ConnectionSource connectionSource;

    public static final String LOG4J_ID_KEY = "log4jid";

    private Thread receiverThread;

    private Job customReceiverJob;

    public void activateOptions() {
        if (connectionSource != null) {
            customReceiverJob = new CustomReceiverJob();

            Scheduler scheduler = LogManager.getSchedulerInstance();
            if (refreshMillis > 0) {
                scheduler.schedule(customReceiverJob, System
                        .currentTimeMillis() + 500, refreshMillis);
            } else {
                scheduler.schedule(customReceiverJob, System
                        .currentTimeMillis() + 500);
            }
        } else {
            throw new IllegalStateException(
                    "CustomSQLDBReceiver cannot function without a connection source");
        }
    }

    void closeConnection(Connection connection) {
        if (connection != null) {
            try {
                // LogLog.warn("closing the connection. ", new Exception("x"));
                connection.close();
            } catch (SQLException sqle) {
                // nothing we can do here
            }
        }
    }

    public void setRefreshMillis(int refreshMillis) {
        this.refreshMillis = refreshMillis;
    }

    public int getRefreshMillis() {
        return refreshMillis;
    }

    /**
     * @return Returns the connectionSource.
     */
    public ConnectionSource getConnectionSource() {
        return connectionSource;
    }

    /**
     * @param connectionSource
     *            The connectionSource to set.
     */
    public void setConnectionSource(ConnectionSource connectionSource) {
        this.connectionSource = connectionSource;
    }

    public void close() {
        try {
            if ((connection != null) && !connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            connection = null;
        }
    }

    public void finalize() {
        close();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.log4j.plugins.Plugin#shutdown()
     */
    public void shutdown() {
        getLogger().info("removing receiverJob from the Scheduler.");

        Scheduler scheduler = LogManager.getSchedulerInstance();
        scheduler.delete(customReceiverJob);
        lastID = -1;
    }

    public void setSql(String s) {
        sqlStatement = s;
    }

    public String getSql() {
        return sqlStatement;
    }

    public void setIDField(String id) {
        idField = id;
    }

    public String getIDField() {
        return idField;
    }

    public synchronized void setPaused(boolean p) {
        paused = p;
    }

    public synchronized boolean isPaused() {
        return paused;
    }

    class CustomReceiverJob implements Job {
        public void execute() {
            Connection connection = null;

            int oldLastID = lastID;
            try {
                connection = connectionSource.getConnection();
                Statement statement = connection.createStatement();

                Logger eventLogger = null;
                long timeStamp = 0L;
                String level = null;
                String threadName = null;
                Object message = null;
                String ndc = null;
                Hashtable mdc = null;
                String[] exception = null;
                String className = null;
                String methodName = null;
                String fileName = null;
                String lineNumber = null;
                Hashtable properties = null;

                String currentSQLStatement = sqlStatement;
                if (whereExists) {
                    currentSQLStatement = sqlStatement + AND_CLAUSE + idField
                            + " > " + lastID;
                } else {
                    currentSQLStatement = sqlStatement + WHERE_CLAUSE + idField
                            + " > " + lastID;
                }

                ResultSet rs = statement.executeQuery(currentSQLStatement);

                int i = 0;
                while (rs.next()) {
                    // add a small break every 1000 received events
                    if (++i == 1000) {
                        synchronized (this) {
                            try {
                                // add a delay
                                wait(300);
                            } catch (InterruptedException ie) {
                            }
                            i = 0;
                        }
                    }
                    eventLogger = Logger.getLogger(rs.getString("LOGGER"));
                    timeStamp = rs.getTimestamp("TIMESTAMP").getTime();

                    level = rs.getString("LEVEL");
                    threadName = rs.getString("THREAD");
                    message = rs.getString("MESSAGE");
                    ndc = rs.getString("NDC");

                    String mdcString = rs.getString("MDC");
                    mdc = new Hashtable();

                    if (mdcString != null) {
                        // support MDC being wrapped in {{name, value}}
                        // or
                        // just name, value
                        if ((mdcString.indexOf("{{") > -1)
                                && (mdcString.indexOf("}}") > -1)) {
                            mdcString = mdcString
                                    .substring(mdcString.indexOf("{{") + 2,
                                            mdcString.indexOf("}}"));
                        }

                        StringTokenizer tok = new StringTokenizer(mdcString,
                                ",");

                        while (tok.countTokens() > 1) {
                            mdc.put(tok.nextToken(), tok.nextToken());
                        }
                    }

                    exception = new String[] { rs.getString("EXCEPTION") };
                    className = rs.getString("CLASS");
                    methodName = rs.getString("METHOD");
                    fileName = rs.getString("FILE");
                    lineNumber = rs.getString("LINE");

                    // if properties are provided in the
                    // SQL they can be used here (for example, to route
                    // events to a unique tab in
                    // Chainsaw if the machinename and/or appname
                    // property
                    // are set)
                    String propertiesString = rs.getString("PROPERTIES");
                    properties = new Hashtable();

                    if (propertiesString != null) {
                        // support properties being wrapped in {{name,
                        // value}} or just name, value
                        if ((propertiesString.indexOf("{{") > -1)
                                && (propertiesString.indexOf("}}") > -1)) {
                            propertiesString = propertiesString.substring(
                                    propertiesString.indexOf("{{") + 2,
                                    propertiesString.indexOf("}}"));
                        }

                        StringTokenizer tok2 = new StringTokenizer(
                                propertiesString, ",");
                        while (tok2.countTokens() > 1) {
                            String name = tok2.nextToken();
                            String value = tok2.nextToken();
                            if (name.equals(LOG4J_ID_KEY)) {
                                try {
                                    int thisInt = Integer.parseInt(value);
                                    value = String.valueOf(thisInt);
                                    if (thisInt > lastID) {
                                        lastID = thisInt;
                                    }
                                } catch (Exception e) {
                                }
                            }
                            properties.put(name, value);
                        }
                    }

                    Level levelImpl = Level.toLevel(level);
                    LoggingEvent event = new LoggingEvent(
                            eventLogger.getName(), eventLogger, levelImpl,
                            message, null);
                    event.setLocationInformation(new LocationInfo(fileName,
                            className, methodName, lineNumber));
                    properties.putAll(mdc);
                    event.setTimeStamp(timeStamp);
                    event.setThrowableInformation(new ThrowableInformation(
                            exception));

                    event.setProperties(properties);
                    event.setThreadName(threadName);
                    event.setNDC(ndc);
                    doPost(event);
                }
                //log when rows are retrieved
                if (lastID != oldLastID) {
                    getLogger().debug("lastID: " + lastID);
                    oldLastID = lastID;
                }

                statement.close();
                statement = null;
            } catch (SQLException sqle) {
                getLogger()
                        .error("*************Problem receiving events", sqle);
            } finally {
                closeConnection(connection);
            }

            // if paused, loop prior to executing sql query
            synchronized (this) {
                while (isPaused()) {
                    try {
                        wait(1000);
                    } catch (InterruptedException ie) {
                    }
                }
            }
        }
    }
}

[See repo JSON]