package com.arpnetworking.utils; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import com.jolbox.bonecp.BoneCPDataSource; import com.jolbox.bonecp.hooks.ConnectionHook; import play.Application; import play.Configuration; import play.Logger; import play.Plugin; import javax.sql.DataSource; import java.util.Iterator; /** * Plugin that starts the standard metrics and stats collection * * @author barp */ public class ArpnetStandard extends Plugin { private final Application _Application; public ArpnetStandard(Application application) { _Application = application; } @Override public void onStart() { Logger.info("Starting Arpnet standard services"); decorateDataSources(_Application); play.api.Logger logger = play.api.Logger.apply("query-log"); org.slf4j.Logger slfLogger = logger.underlyingLogger(); boolean directLog = false; if (slfLogger instanceof ch.qos.logback.classic.Logger) { final ch.qos.logback.classic.Logger logbackLogger = (ch.qos.logback.classic.Logger) slfLogger; LoggerContext context = logbackLogger.getLoggerContext(); for (Appender<ILoggingEvent> appender : new Iterable<Appender<ILoggingEvent>>() { @Override public Iterator<Appender<ILoggingEvent>> iterator() { return logbackLogger.iteratorForAppenders(); } }) { directLog = true; } if (!directLog) { Logger.warn("It appears that there is not a logger setup for query logs. For query logs to work, they must be directly written to an independent file."); Logger.warn("Add the following to your logger.xml file:"); Logger.warn("\n" + " <appender name=\"QUERY-LOG-FILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n" + " <file>${application.home}/logs/query-log.log</file>\n" + " <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n" + " <!-- hourly rollover -->\n" + " <fileNamePattern>${application.home}/logs/query-log.%d{yy-MM-dd_HH}.log</fileNamePattern>\n" + " <!-- keep 30 days of history -->\n" + " <maxHistory>720</maxHistory>\n" + " </rollingPolicy>\n" + " <encoder>\n" + " <pattern>%message%n</pattern>\n" + " </encoder>\n" + " </appender>" + " <appender name=\"ASYNCQL\" class=\"ch.qos.logback.classic.AsyncAppender\">\n" + " <appender-ref ref=\"QUERY-LOG-FILE\" />\n" + " </appender>" + " <logger name=\"query-log\" level=\"INFO\" additivity=\"false\">\n" + " <appender-ref ref=\"ASYNCQL\"/>\n" + " </logger>"); } } else { Logger.warn("Unknown logging platform, cannot determine if query logs will work"); } } @Override public void onStop() { } private void decorateDataSources(Application app) { Configuration dbConfig = app.configuration().getConfig("db"); Configuration ebeanConf = app.configuration().getConfig("ebean"); if (dbConfig == null) { return; } for (String dbName : dbConfig.subKeys()) { DataSource datasource = play.db.DB.getDataSource(dbName); if (datasource instanceof BoneCPDataSource) { BoneCPDataSource bcpDs = (BoneCPDataSource) datasource; ConnectionHook originalHook = bcpDs.getConnectionHook(); bcpDs.setConnectionHook(new MetricLoggingHook(originalHook, dbName)); } final String dbEbeanConf = ebeanConf.getString(dbName); boolean foundModel = false; if (dbEbeanConf != null) { if (dbEbeanConf.contains("com.avaje.ebean.meta.*") && (dbEbeanConf.contains("com.arpnetworking.utils.models.*") || dbEbeanConf.contains("com.arpnetworking.utils.models.QueryStatisticFinder"))) { foundModel = true; } if (!foundModel) { String modified = dbEbeanConf + ",com.avaje.ebean.meta.*,com.arpnetworking.utils.models.*"; Logger.warn("Database \"" + dbName + "\" models not loaded properly. ArpnetStandard plugin requires specific models to be loaded into Ebean."); Logger.warn("Recommended model entry is: ebean." + dbName + "=\"" + modified + "\""); } } } } }