P2JQueryStatisticsConfiguration.java
/*
** Module : P2JQueryStatisticsConfiguration.java
** Abstract : Configure P2JQueryLogger settings
**
** Copyright (c) 2023-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
** 001 RAA 20230802 First revision with basic usage.
** 002 ICP 20240419 Added support for logging in a h2 database.
** ICP 20240813 Instead of using a FileHandler, the CentralLoggerFile is used now
** to log the P2J queries.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.persist;
import static com.goldencode.p2j.util.logging.LoggingUtil.resolveLogFile;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import java.util.logging.*;
import com.goldencode.p2j.directory.DirectoryService;
import com.goldencode.p2j.main.ClientCore;
import com.goldencode.p2j.util.Utils;
import com.goldencode.p2j.util.logging.*;
/**
* Class that stores the P2J query logging configurations per-database.
*/
public class P2JQueryStatisticsConfiguration
{
/** Map that includes a configuration for each given database. */
private static final Map<String, P2JQueryStatisticsConfiguration> configs = new HashMap<>();
/** Logger. */
private static final CentralLogger LOG = CentralLogger.get(P2JQueryStatisticsConfiguration.class);
/** The log formatter. */
private static final Formatter LOG_FORMATTER = new CentralLogFormatter();
/** Global directory path, used if a local one does not exist. */
private static final String GLOBAL_DIR_PATH = "persistence/p2j-query-logging";
/** The type of logging intended. */
private final LoggingType loggingType;
/** The file logger, responsible for writing logs to files. */
private static CentralLoggerFile fileLogger;
/**
* Flag that determines whether the log output should contain the stack trace or not.
* The stack trace will contain the name of the file (and line number) from outside the P2J package.
*/
private final boolean STACK_TRACING;
/**
* Flag that determines whether the log output should contain the location of the query or not.
* It will contain the name of the file (and line number) from inside the P2J package.
*/
private final boolean LOCATION;
/** Display the results every "x" processed components. By default, the result will be shown
* every time a component is processed.
*/
private final int PROCESSED_COMPONENTS;
/** Only queries that are slower than this value will be logged. */
private final double MIN_TIME_THRESHOLD;
/** The file that will receive the logging output. */
private final String OUTPUT_FILE;
/** The file name for the h2 database that will receive the logging output . */
private final String OUTPUT_DB;
/** The user for the h2 database that will receive the logging output . */
private final String USER_DB;
/** The password for the h2 database that will receive the logging output . */
private final String PASS_DB;
/** The helper object to facilitate logging to H2 database*/
private P2JLoggingDatabaseHelper dbHelper;
/**
* Constructor that sets the logging configuration per-database.
*
* @param databaseName
* The name of the database in use.
*/
private P2JQueryStatisticsConfiguration(String databaseName)
{
boolean enabled = false;
boolean stackTracing = false;
boolean location = false;
boolean hasLocalConfiguration = false;
boolean outputDbEnabled = false;
boolean profilingEnabled = false;
int processedComponents = 1;
double minTime = 0;
String outputFile = null;
String defaultOutputFile = "p2j-query-logging_%db_" +
LoggingUtil.SORTABLE_DATETIME_FORMAT +
"_%uos_%pid_%as.log";
String outputDb = null;
String userDb = null;
String passDb = null;
String defaultOutputDb = "p2j_logging_%db_" +
LoggingUtil.SORTABLE_DATETIME_FORMAT +
"_%uos_%pid_%as";
String defaultUserDb = "";
String defaultPassDb = "";
String directoryPath = null;
String localDirectoryPath = "database/" + databaseName + "/p2j-query-logging";
DirectoryService ds = DirectoryService.getInstance();
if (ds != null)
{
try
{
if (!ds.bind())
{
LOG.warning("P2J query logging is not enabled");
}
String isLocallyEnabled = Utils.getDirectoryNodeString(ds,
localDirectoryPath + "/enabled",
"NULL",
false);
switch (isLocallyEnabled.toUpperCase())
{
case "NULL":
String globalDirectoryPath = GLOBAL_DIR_PATH + "/enabled";
enabled = Boolean.parseBoolean(Utils.getDirectoryNodeString(ds,
globalDirectoryPath,
"FALSE",
false));
break;
case "TRUE":
enabled = true;
hasLocalConfiguration = true;
break;
case "FALSE":
enabled = false;
hasLocalConfiguration = true;
break;
default:
throw new RuntimeException("Invalid value for attribute: enabled");
}
if (enabled)
{
directoryPath = hasLocalConfiguration ? localDirectoryPath : GLOBAL_DIR_PATH;
profilingEnabled = Utils.getDirectoryNodeBoolean(ds,
directoryPath + "/profiling",
false,
false);
if (profilingEnabled)
{
minTime = Utils.getDirectoryNodeDouble(ds,
directoryPath + "/min-time-threshold",
0,
false);
}
outputDbEnabled = Utils.getDirectoryNodeBoolean(ds,
directoryPath + "/output-to-db/enabled",
false,
false);
if (outputDbEnabled)
{
outputDb = Utils.getDirectoryNodeString(ds,
directoryPath + "/output-to-db/db-name",
defaultOutputDb,
false);
userDb = Utils.getDirectoryNodeString(ds,
directoryPath + "/output-to-db/db-user",
defaultUserDb,
false);
passDb = Utils.getDirectoryNodeString(ds,
directoryPath + "/output-to-db/db-pass",
defaultPassDb,
false);
}
stackTracing = Utils.getDirectoryNodeBoolean(ds,
directoryPath + "/stack-tracing",
false,
false);
location = Utils.getDirectoryNodeBoolean(ds,
directoryPath + "/location",
false,
false);
processedComponents = Utils.getDirectoryNodeInt(ds,
directoryPath + "/processed-components",
1,
false);
outputFile = Utils.getDirectoryNodeString(ds,
directoryPath + "/output-to-file",
defaultOutputFile,
false);
}
}
finally
{
ds.unbind();
}
}
MIN_TIME_THRESHOLD = minTime;
STACK_TRACING = stackTracing;
LOCATION = location;
OUTPUT_FILE = outputFile;
OUTPUT_DB = outputDb;
USER_DB = userDb;
PASS_DB = passDb;
PROCESSED_COMPONENTS = processedComponents;
if (!enabled)
{
loggingType = LoggingType.NONE;
return;
}
loggingType = profilingEnabled ? LoggingType.PROFILING : LoggingType.LOGGING;
Supplier<Long> processIdSupplier = () -> {
try
{
System.loadLibrary("p2j");
return ClientCore.getPid();
}
catch (Throwable t)
{
LOG.log(Level.FINE, "", t);
return 0L;
}
};
String formattedFilePath = resolveLogFile(OUTPUT_FILE,
processIdSupplier,
"server",
databaseName,
true);
fileLogger = CentralLoggerFile.initialize("p2j_" + databaseName + "_logger", formattedFilePath, 0, 0);
if (outputDbEnabled)
{
String formattedDBPath = resolveLogFile(OUTPUT_DB,
processIdSupplier,
"server",
databaseName,
true);
dbHelper = P2JLoggingDatabaseHelper.getDbHelper(databaseName, formattedDBPath, USER_DB, PASS_DB);
}
}
/**
* Get the logging configuration of a specific database.
*
* @param databaseName
* The name of the database in use.
*
* @return P2JQueryStatisticsConfiguration
* If it exists, an already created configuration for that database, or a new one otherwise.
*/
public static P2JQueryStatisticsConfiguration getConfiguration(String databaseName)
{
P2JQueryStatisticsConfiguration config = configs.get(databaseName);
if (config != null)
{
return config;
}
config = new P2JQueryStatisticsConfiguration(databaseName);
configs.put(databaseName, config);
return config;
}
/**
* Getter for the {@code loggingType} field.
*
* @return LoggingType
* The type of logger.
*/
public LoggingType getLoggingType()
{
return loggingType;
}
/**
* Getter for the {@code STACK_TRACING} field.
*
* @return boolean
* The value of {@code STACK_TRACING}.
*/
public boolean isStackTracingEnabled()
{
return STACK_TRACING;
}
/**
* Getter for the {@code LOCATION} field.
*
* @return boolean
* The value of {@code LOCATION}.
*/
public boolean isLocationEnabled()
{
return LOCATION;
}
/**
* Getter for the {@code PROCESSED_COMPONENTS} field.
*
* @return int
* The value of {@code PROCESSED_COMPONENTS}.
*/
public int getProcessedComponents()
{
return PROCESSED_COMPONENTS;
}
/**
* Getter for the {@code MIN_TIME_THRESHOLD} field.
*
* @return double
* The value of {@code MIN_TIME_THRESHOLD}.
*/
public double getMinTimeThreshold()
{
return MIN_TIME_THRESHOLD;
}
/**
* Getter for the {@code dbHelper} field.
*
* @return The value of {@code dbHelper}.
*/
public P2JLoggingDatabaseHelper getDbHelper()
{
return dbHelper;
}
/**
* Enum denoting what should happen with a statement: should it just be executed or the intention is to
* log things about it?
*/
public enum LoggingType
{
/** Statement should be executed without logging anything. */
NONE,
/** Logging is enabled. */
LOGGING,
/** More than just logging, the statement should also be profiled. */
PROFILING;
}
/**
* Getter for the {@code fileLogger} field.
*
* @return The value of {@code fileLogger}.
*/
public CentralLoggerFile getFileLogger()
{
return fileLogger;
}
}