|
|
68bf46 |
From 6370372f5a04cdec6598fa8199b762ce33fa4d40 Mon Sep 17 00:00:00 2001
|
|
|
68bf46 |
From: Mikolaj Izdebski <mizdebsk@redhat.com>
|
|
|
68bf46 |
Date: Wed, 2 Feb 2022 19:37:17 +0100
|
|
|
68bf46 |
Subject: [PATCH] Fix CVE-2022-23305 JDBCAppender
|
|
|
68bf46 |
|
|
|
68bf46 |
---
|
|
|
68bf46 |
.../apache/log4j/helpers/PatternParser.java | 944 +++++++++---------
|
|
|
68bf46 |
.../org/apache/log4j/jdbc/JDBCAppender.java | 715 ++++++-------
|
|
|
68bf46 |
.../apache/log4j/jdbc/JdbcPatternParser.java | 115 +++
|
|
|
68bf46 |
3 files changed, 942 insertions(+), 832 deletions(-)
|
|
|
68bf46 |
create mode 100644 src/main/java/org/apache/log4j/jdbc/JdbcPatternParser.java
|
|
|
68bf46 |
|
|
|
68bf46 |
diff --git a/src/main/java/org/apache/log4j/helpers/PatternParser.java b/src/main/java/org/apache/log4j/helpers/PatternParser.java
|
|
|
68bf46 |
index 0d3ead67..4b169a2d 100644
|
|
|
68bf46 |
--- a/src/main/java/org/apache/log4j/helpers/PatternParser.java
|
|
|
68bf46 |
+++ b/src/main/java/org/apache/log4j/helpers/PatternParser.java
|
|
|
68bf46 |
@@ -30,541 +30,503 @@ import java.util.Arrays;
|
|
|
68bf46 |
// Reinhard Deschler <reinhard.deschler@web.de>
|
|
|
68bf46 |
|
|
|
68bf46 |
/**
|
|
|
68bf46 |
- Most of the work of the {@link org.apache.log4j.PatternLayout} class
|
|
|
68bf46 |
- is delegated to the PatternParser class.
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- It is this class that parses conversion patterns and creates
|
|
|
68bf46 |
- a chained list of {@link OptionConverter OptionConverters}.
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- @author James P. Cakalic
|
|
|
68bf46 |
- @author Ceki Gülcü
|
|
|
68bf46 |
- @author Anders Kristensen
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- @since 0.8.2
|
|
|
68bf46 |
-*/
|
|
|
68bf46 |
+ * Most of the work of the {@link org.apache.log4j.PatternLayout} class is
|
|
|
68bf46 |
+ * delegated to the PatternParser class.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * It is this class that parses conversion patterns and creates a chained list
|
|
|
68bf46 |
+ * of {@link OptionConverter OptionConverters}.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * @author James P. Cakalic
|
|
|
68bf46 |
+ * @author Ceki Gülcü
|
|
|
68bf46 |
+ * @author Anders Kristensen
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * @since 0.8.2
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
public class PatternParser {
|
|
|
68bf46 |
|
|
|
68bf46 |
- private static final char ESCAPE_CHAR = '%';
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- private static final int LITERAL_STATE = 0;
|
|
|
68bf46 |
- private static final int CONVERTER_STATE = 1;
|
|
|
68bf46 |
- private static final int DOT_STATE = 3;
|
|
|
68bf46 |
- private static final int MIN_STATE = 4;
|
|
|
68bf46 |
- private static final int MAX_STATE = 5;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- static final int FULL_LOCATION_CONVERTER = 1000;
|
|
|
68bf46 |
- static final int METHOD_LOCATION_CONVERTER = 1001;
|
|
|
68bf46 |
- static final int CLASS_LOCATION_CONVERTER = 1002;
|
|
|
68bf46 |
- static final int LINE_LOCATION_CONVERTER = 1003;
|
|
|
68bf46 |
- static final int FILE_LOCATION_CONVERTER = 1004;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- static final int RELATIVE_TIME_CONVERTER = 2000;
|
|
|
68bf46 |
- static final int THREAD_CONVERTER = 2001;
|
|
|
68bf46 |
- static final int LEVEL_CONVERTER = 2002;
|
|
|
68bf46 |
- static final int NDC_CONVERTER = 2003;
|
|
|
68bf46 |
- static final int MESSAGE_CONVERTER = 2004;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- int state;
|
|
|
68bf46 |
- protected StringBuffer currentLiteral = new StringBuffer(32);
|
|
|
68bf46 |
- protected int patternLength;
|
|
|
68bf46 |
- protected int i;
|
|
|
68bf46 |
- PatternConverter head;
|
|
|
68bf46 |
- PatternConverter tail;
|
|
|
68bf46 |
- protected FormattingInfo formattingInfo = new FormattingInfo();
|
|
|
68bf46 |
- protected String pattern;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- public
|
|
|
68bf46 |
- PatternParser(String pattern) {
|
|
|
68bf46 |
- this.pattern = pattern;
|
|
|
68bf46 |
- patternLength = pattern.length();
|
|
|
68bf46 |
- state = LITERAL_STATE;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- private
|
|
|
68bf46 |
- void addToList(PatternConverter pc) {
|
|
|
68bf46 |
- if(head == null) {
|
|
|
68bf46 |
- head = tail = pc;
|
|
|
68bf46 |
- } else {
|
|
|
68bf46 |
- tail.next = pc;
|
|
|
68bf46 |
- tail = pc;
|
|
|
68bf46 |
+ private static final char LEFT_BRACKET = '{';
|
|
|
68bf46 |
+ private static final char RIGHT_BRACKET = '}';
|
|
|
68bf46 |
+ private static final char N_CHAR = 'n';
|
|
|
68bf46 |
+ private static final char DOT_CHAR = '.';
|
|
|
68bf46 |
+ private static final char DASH_CHAR = '-';
|
|
|
68bf46 |
+ private static final char ESCAPE_CHAR = '%';
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private static final int LITERAL_STATE = 0;
|
|
|
68bf46 |
+ private static final int CONVERTER_STATE = 1;
|
|
|
68bf46 |
+ private static final int DOT_STATE = 3;
|
|
|
68bf46 |
+ private static final int MIN_STATE = 4;
|
|
|
68bf46 |
+ private static final int MAX_STATE = 5;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ static final int FULL_LOCATION_CONVERTER = 1000;
|
|
|
68bf46 |
+ static final int METHOD_LOCATION_CONVERTER = 1001;
|
|
|
68bf46 |
+ static final int CLASS_LOCATION_CONVERTER = 1002;
|
|
|
68bf46 |
+ static final int LINE_LOCATION_CONVERTER = 1003;
|
|
|
68bf46 |
+ static final int FILE_LOCATION_CONVERTER = 1004;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ static final int RELATIVE_TIME_CONVERTER = 2000;
|
|
|
68bf46 |
+ static final int THREAD_CONVERTER = 2001;
|
|
|
68bf46 |
+ static final int LEVEL_CONVERTER = 2002;
|
|
|
68bf46 |
+ static final int NDC_CONVERTER = 2003;
|
|
|
68bf46 |
+ static final int MESSAGE_CONVERTER = 2004;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ int state;
|
|
|
68bf46 |
+ protected StringBuffer currentLiteral = new StringBuffer(32);
|
|
|
68bf46 |
+ protected int patternLength;
|
|
|
68bf46 |
+ protected int i;
|
|
|
68bf46 |
+ PatternConverter head;
|
|
|
68bf46 |
+ PatternConverter tail;
|
|
|
68bf46 |
+ protected FormattingInfo formattingInfo = new FormattingInfo();
|
|
|
68bf46 |
+ protected String pattern;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ public PatternParser(String pattern) {
|
|
|
68bf46 |
+ this.pattern = pattern;
|
|
|
68bf46 |
+ patternLength = pattern.length();
|
|
|
68bf46 |
+ state = LITERAL_STATE;
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- protected
|
|
|
68bf46 |
- String extractOption() {
|
|
|
68bf46 |
- if((i < patternLength) && (pattern.charAt(i) == '{')) {
|
|
|
68bf46 |
- int end = pattern.indexOf('}', i);
|
|
|
68bf46 |
- if (end > i) {
|
|
|
68bf46 |
- String r = pattern.substring(i + 1, end);
|
|
|
68bf46 |
- i = end+1;
|
|
|
68bf46 |
- return r;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- return null;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- The option is expected to be in decimal and positive. In case of
|
|
|
68bf46 |
- error, zero is returned. */
|
|
|
68bf46 |
- protected
|
|
|
68bf46 |
- int extractPrecisionOption() {
|
|
|
68bf46 |
- String opt = extractOption();
|
|
|
68bf46 |
- int r = 0;
|
|
|
68bf46 |
- if(opt != null) {
|
|
|
68bf46 |
- try {
|
|
|
68bf46 |
- r = Integer.parseInt(opt);
|
|
|
68bf46 |
- if(r <= 0) {
|
|
|
68bf46 |
- LogLog.error(
|
|
|
68bf46 |
- "Precision option (" + opt + ") isn't a positive integer.");
|
|
|
68bf46 |
- r = 0;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private void addToList(PatternConverter pc) {
|
|
|
68bf46 |
+ if (head == null) {
|
|
|
68bf46 |
+ head = tail = pc;
|
|
|
68bf46 |
+ } else {
|
|
|
68bf46 |
+ tail.next = pc;
|
|
|
68bf46 |
+ tail = pc;
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- catch (NumberFormatException e) {
|
|
|
68bf46 |
- LogLog.error("Category option \""+opt+"\" not a decimal integer.", e);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- return r;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- public
|
|
|
68bf46 |
- PatternConverter parse() {
|
|
|
68bf46 |
- char c;
|
|
|
68bf46 |
- i = 0;
|
|
|
68bf46 |
- while(i < patternLength) {
|
|
|
68bf46 |
- c = pattern.charAt(i++);
|
|
|
68bf46 |
- switch(state) {
|
|
|
68bf46 |
- case LITERAL_STATE:
|
|
|
68bf46 |
- // In literal state, the last char is always a literal.
|
|
|
68bf46 |
- if(i == patternLength) {
|
|
|
68bf46 |
- currentLiteral.append(c);
|
|
|
68bf46 |
- continue;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- if(c == ESCAPE_CHAR) {
|
|
|
68bf46 |
- // peek at the next char.
|
|
|
68bf46 |
- switch(pattern.charAt(i)) {
|
|
|
68bf46 |
- case ESCAPE_CHAR:
|
|
|
68bf46 |
- currentLiteral.append(c);
|
|
|
68bf46 |
- i++; // move pointer
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'n':
|
|
|
68bf46 |
- currentLiteral.append(Layout.LINE_SEP);
|
|
|
68bf46 |
- i++; // move pointer
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- default:
|
|
|
68bf46 |
- if(currentLiteral.length() != 0) {
|
|
|
68bf46 |
- addToList(new LiteralPatternConverter(
|
|
|
68bf46 |
- currentLiteral.toString()));
|
|
|
68bf46 |
- //LogLog.debug("Parsed LITERAL converter: \""
|
|
|
68bf46 |
- // +currentLiteral+"\".");
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- currentLiteral.append(c); // append %
|
|
|
68bf46 |
- state = CONVERTER_STATE;
|
|
|
68bf46 |
- formattingInfo.reset();
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- else {
|
|
|
68bf46 |
- currentLiteral.append(c);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case CONVERTER_STATE:
|
|
|
68bf46 |
- currentLiteral.append(c);
|
|
|
68bf46 |
- switch(c) {
|
|
|
68bf46 |
- case '-':
|
|
|
68bf46 |
- formattingInfo.leftAlign = true;
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case '.':
|
|
|
68bf46 |
- state = DOT_STATE;
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- default:
|
|
|
68bf46 |
- if(c >= '0' && c <= '9') {
|
|
|
68bf46 |
- formattingInfo.min = c - '0';
|
|
|
68bf46 |
- state = MIN_STATE;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- else
|
|
|
68bf46 |
- finalizeConverter(c);
|
|
|
68bf46 |
- } // switch
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case MIN_STATE:
|
|
|
68bf46 |
- currentLiteral.append(c);
|
|
|
68bf46 |
- if(c >= '0' && c <= '9')
|
|
|
68bf46 |
- formattingInfo.min = formattingInfo.min*10 + (c - '0');
|
|
|
68bf46 |
- else if(c == '.')
|
|
|
68bf46 |
- state = DOT_STATE;
|
|
|
68bf46 |
- else {
|
|
|
68bf46 |
- finalizeConverter(c);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case DOT_STATE:
|
|
|
68bf46 |
- currentLiteral.append(c);
|
|
|
68bf46 |
- if(c >= '0' && c <= '9') {
|
|
|
68bf46 |
- formattingInfo.max = c - '0';
|
|
|
68bf46 |
- state = MAX_STATE;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- else {
|
|
|
68bf46 |
- LogLog.error("Error occured in position "+i
|
|
|
68bf46 |
- +".\n Was expecting digit, instead got char \""+c+"\".");
|
|
|
68bf46 |
- state = LITERAL_STATE;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case MAX_STATE:
|
|
|
68bf46 |
- currentLiteral.append(c);
|
|
|
68bf46 |
- if(c >= '0' && c <= '9')
|
|
|
68bf46 |
- formattingInfo.max = formattingInfo.max*10 + (c - '0');
|
|
|
68bf46 |
- else {
|
|
|
68bf46 |
- finalizeConverter(c);
|
|
|
68bf46 |
- state = LITERAL_STATE;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ protected String extractOption() {
|
|
|
68bf46 |
+ if ((i < patternLength) && (pattern.charAt(i) == LEFT_BRACKET)) {
|
|
|
68bf46 |
+ int end = pattern.indexOf(RIGHT_BRACKET, i);
|
|
|
68bf46 |
+ if (end > i) {
|
|
|
68bf46 |
+ String r = pattern.substring(i + 1, end);
|
|
|
68bf46 |
+ i = end + 1;
|
|
|
68bf46 |
+ return r;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- } // switch
|
|
|
68bf46 |
- } // while
|
|
|
68bf46 |
- if(currentLiteral.length() != 0) {
|
|
|
68bf46 |
- addToList(new LiteralPatternConverter(currentLiteral.toString()));
|
|
|
68bf46 |
- //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
|
|
|
68bf46 |
+ return null;
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- return head;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- protected
|
|
|
68bf46 |
- void finalizeConverter(char c) {
|
|
|
68bf46 |
- PatternConverter pc = null;
|
|
|
68bf46 |
- switch(c) {
|
|
|
68bf46 |
- case 'c':
|
|
|
68bf46 |
- pc = new CategoryPatternConverter(formattingInfo,
|
|
|
68bf46 |
- extractPrecisionOption());
|
|
|
68bf46 |
- //LogLog.debug("CATEGORY converter.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'C':
|
|
|
68bf46 |
- pc = new ClassNamePatternConverter(formattingInfo,
|
|
|
68bf46 |
- extractPrecisionOption());
|
|
|
68bf46 |
- //LogLog.debug("CLASS_NAME converter.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'd':
|
|
|
68bf46 |
- String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
|
|
|
68bf46 |
- DateFormat df;
|
|
|
68bf46 |
- String dOpt = extractOption();
|
|
|
68bf46 |
- if(dOpt != null)
|
|
|
68bf46 |
- dateFormatStr = dOpt;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- if(dateFormatStr.equalsIgnoreCase(
|
|
|
68bf46 |
- AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
|
|
|
68bf46 |
- df = new ISO8601DateFormat();
|
|
|
68bf46 |
- else if(dateFormatStr.equalsIgnoreCase(
|
|
|
68bf46 |
- AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
|
|
|
68bf46 |
- df = new AbsoluteTimeDateFormat();
|
|
|
68bf46 |
- else if(dateFormatStr.equalsIgnoreCase(
|
|
|
68bf46 |
- AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
|
|
|
68bf46 |
- df = new DateTimeDateFormat();
|
|
|
68bf46 |
- else {
|
|
|
68bf46 |
- try {
|
|
|
68bf46 |
- df = new SimpleDateFormat(dateFormatStr);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- catch (IllegalArgumentException e) {
|
|
|
68bf46 |
- LogLog.error("Could not instantiate SimpleDateFormat with " +
|
|
|
68bf46 |
- dateFormatStr, e);
|
|
|
68bf46 |
- df = (DateFormat) OptionConverter.instantiateByClassName(
|
|
|
68bf46 |
- "org.apache.log4j.helpers.ISO8601DateFormat",
|
|
|
68bf46 |
- DateFormat.class, null);
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * The option is expected to be in decimal and positive. In case of error, zero
|
|
|
68bf46 |
+ * is returned.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected int extractPrecisionOption() {
|
|
|
68bf46 |
+ String opt = extractOption();
|
|
|
68bf46 |
+ int r = 0;
|
|
|
68bf46 |
+ if (opt != null) {
|
|
|
68bf46 |
+ try {
|
|
|
68bf46 |
+ r = Integer.parseInt(opt);
|
|
|
68bf46 |
+ if (r <= 0) {
|
|
|
68bf46 |
+ LogLog.error("Precision option (" + opt + ") isn't a positive integer.");
|
|
|
68bf46 |
+ r = 0;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ } catch (NumberFormatException e) {
|
|
|
68bf46 |
+ LogLog.error("Category option \"" + opt + "\" not a decimal integer.", e);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- pc = new DatePatternConverter(formattingInfo, df);
|
|
|
68bf46 |
- //LogLog.debug("DATE converter {"+dateFormatStr+"}.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'F':
|
|
|
68bf46 |
- pc = new LocationPatternConverter(formattingInfo,
|
|
|
68bf46 |
- FILE_LOCATION_CONVERTER);
|
|
|
68bf46 |
- //LogLog.debug("File name converter.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'l':
|
|
|
68bf46 |
- pc = new LocationPatternConverter(formattingInfo,
|
|
|
68bf46 |
- FULL_LOCATION_CONVERTER);
|
|
|
68bf46 |
- //LogLog.debug("Location converter.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'L':
|
|
|
68bf46 |
- pc = new LocationPatternConverter(formattingInfo,
|
|
|
68bf46 |
- LINE_LOCATION_CONVERTER);
|
|
|
68bf46 |
- //LogLog.debug("LINE NUMBER converter.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'm':
|
|
|
68bf46 |
- pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
|
|
|
68bf46 |
- //LogLog.debug("MESSAGE converter.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'M':
|
|
|
68bf46 |
- pc = new LocationPatternConverter(formattingInfo,
|
|
|
68bf46 |
- METHOD_LOCATION_CONVERTER);
|
|
|
68bf46 |
- //LogLog.debug("METHOD converter.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'p':
|
|
|
68bf46 |
- pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
|
|
|
68bf46 |
- //LogLog.debug("LEVEL converter.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'r':
|
|
|
68bf46 |
- pc = new BasicPatternConverter(formattingInfo,
|
|
|
68bf46 |
- RELATIVE_TIME_CONVERTER);
|
|
|
68bf46 |
- //LogLog.debug("RELATIVE time converter.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 't':
|
|
|
68bf46 |
- pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
|
|
|
68bf46 |
- //LogLog.debug("THREAD converter.");
|
|
|
68bf46 |
- //formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- /*case 'u':
|
|
|
68bf46 |
- if(i < patternLength) {
|
|
|
68bf46 |
- char cNext = pattern.charAt(i);
|
|
|
68bf46 |
- if(cNext >= '0' && cNext <= '9') {
|
|
|
68bf46 |
- pc = new UserFieldPatternConverter(formattingInfo, cNext - '0');
|
|
|
68bf46 |
- LogLog.debug("USER converter ["+cNext+"].");
|
|
|
68bf46 |
- formattingInfo.dump();
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- i++;
|
|
|
68bf46 |
+ return r;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ public PatternConverter parse() {
|
|
|
68bf46 |
+ char c;
|
|
|
68bf46 |
+ i = 0;
|
|
|
68bf46 |
+ while (i < patternLength) {
|
|
|
68bf46 |
+ c = pattern.charAt(i++);
|
|
|
68bf46 |
+ switch (state) {
|
|
|
68bf46 |
+ case LITERAL_STATE:
|
|
|
68bf46 |
+ // In literal state, the last char is always a literal.
|
|
|
68bf46 |
+ if (i == patternLength) {
|
|
|
68bf46 |
+ currentLiteral.append(c);
|
|
|
68bf46 |
+ continue;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ if (c == ESCAPE_CHAR) {
|
|
|
68bf46 |
+ // peek at the next char.
|
|
|
68bf46 |
+ switch (pattern.charAt(i)) {
|
|
|
68bf46 |
+ case ESCAPE_CHAR:
|
|
|
68bf46 |
+ currentLiteral.append(c);
|
|
|
68bf46 |
+ i++; // move pointer
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case N_CHAR:
|
|
|
68bf46 |
+ currentLiteral.append(Layout.LINE_SEP);
|
|
|
68bf46 |
+ i++; // move pointer
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ default:
|
|
|
68bf46 |
+ if (currentLiteral.length() != 0) {
|
|
|
68bf46 |
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
|
|
|
68bf46 |
+ // LogLog.debug("Parsed LITERAL converter: \""
|
|
|
68bf46 |
+ // +currentLiteral+"\".");
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ currentLiteral.append(c); // append %
|
|
|
68bf46 |
+ state = CONVERTER_STATE;
|
|
|
68bf46 |
+ formattingInfo.reset();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ } else {
|
|
|
68bf46 |
+ currentLiteral.append(c);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case CONVERTER_STATE:
|
|
|
68bf46 |
+ currentLiteral.append(c);
|
|
|
68bf46 |
+ switch (c) {
|
|
|
68bf46 |
+ case DASH_CHAR:
|
|
|
68bf46 |
+ formattingInfo.leftAlign = true;
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case DOT_CHAR:
|
|
|
68bf46 |
+ state = DOT_STATE;
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ default:
|
|
|
68bf46 |
+ if (c >= '0' && c <= '9') {
|
|
|
68bf46 |
+ formattingInfo.min = c - '0';
|
|
|
68bf46 |
+ state = MIN_STATE;
|
|
|
68bf46 |
+ } else
|
|
|
68bf46 |
+ finalizeConverter(c);
|
|
|
68bf46 |
+ } // switch
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case MIN_STATE:
|
|
|
68bf46 |
+ currentLiteral.append(c);
|
|
|
68bf46 |
+ if (c >= '0' && c <= '9')
|
|
|
68bf46 |
+ formattingInfo.min = formattingInfo.min * 10 + (c - '0');
|
|
|
68bf46 |
+ else if (c == '.')
|
|
|
68bf46 |
+ state = DOT_STATE;
|
|
|
68bf46 |
+ else {
|
|
|
68bf46 |
+ finalizeConverter(c);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case DOT_STATE:
|
|
|
68bf46 |
+ currentLiteral.append(c);
|
|
|
68bf46 |
+ if (c >= '0' && c <= '9') {
|
|
|
68bf46 |
+ formattingInfo.max = c - '0';
|
|
|
68bf46 |
+ state = MAX_STATE;
|
|
|
68bf46 |
+ } else {
|
|
|
68bf46 |
+ LogLog.error("Error occured in position " + i + ".\n Was expecting digit, instead got char \"" + c
|
|
|
68bf46 |
+ + "\".");
|
|
|
68bf46 |
+ state = LITERAL_STATE;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case MAX_STATE:
|
|
|
68bf46 |
+ currentLiteral.append(c);
|
|
|
68bf46 |
+ if (c >= '0' && c <= '9')
|
|
|
68bf46 |
+ formattingInfo.max = formattingInfo.max * 10 + (c - '0');
|
|
|
68bf46 |
+ else {
|
|
|
68bf46 |
+ finalizeConverter(c);
|
|
|
68bf46 |
+ state = LITERAL_STATE;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ } // switch
|
|
|
68bf46 |
+ } // while
|
|
|
68bf46 |
+ if (currentLiteral.length() != 0) {
|
|
|
68bf46 |
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
|
|
|
68bf46 |
+ // LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- else
|
|
|
68bf46 |
- LogLog.error("Unexpected char" +cNext+" at position "+i);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- break;*/
|
|
|
68bf46 |
- case 'x':
|
|
|
68bf46 |
- pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
|
|
|
68bf46 |
- //LogLog.debug("NDC converter.");
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- case 'X':
|
|
|
68bf46 |
- String xOpt = extractOption();
|
|
|
68bf46 |
- pc = new MDCPatternConverter(formattingInfo, xOpt);
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- break;
|
|
|
68bf46 |
- default:
|
|
|
68bf46 |
- LogLog.error("Unexpected char [" +c+"] at position "+i
|
|
|
68bf46 |
- +" in conversion patterrn.");
|
|
|
68bf46 |
- pc = new LiteralPatternConverter(currentLiteral.toString());
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
+ return head;
|
|
|
68bf46 |
}
|
|
|
68bf46 |
|
|
|
68bf46 |
- addConverter(pc);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- protected
|
|
|
68bf46 |
- void addConverter(PatternConverter pc) {
|
|
|
68bf46 |
- currentLiteral.setLength(0);
|
|
|
68bf46 |
- // Add the pattern converter to the list.
|
|
|
68bf46 |
- addToList(pc);
|
|
|
68bf46 |
- // Next pattern is assumed to be a literal.
|
|
|
68bf46 |
- state = LITERAL_STATE;
|
|
|
68bf46 |
- // Reset formatting info
|
|
|
68bf46 |
- formattingInfo.reset();
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- // ---------------------------------------------------------------------
|
|
|
68bf46 |
- // PatternConverters
|
|
|
68bf46 |
- // ---------------------------------------------------------------------
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- private static class BasicPatternConverter extends PatternConverter {
|
|
|
68bf46 |
- int type;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- BasicPatternConverter(FormattingInfo formattingInfo, int type) {
|
|
|
68bf46 |
- super(formattingInfo);
|
|
|
68bf46 |
- this.type = type;
|
|
|
68bf46 |
+ protected void finalizeConverter(char c) {
|
|
|
68bf46 |
+ PatternConverter pc = null;
|
|
|
68bf46 |
+ switch (c) {
|
|
|
68bf46 |
+ case 'c':
|
|
|
68bf46 |
+ pc = new CategoryPatternConverter(formattingInfo, extractPrecisionOption());
|
|
|
68bf46 |
+ // LogLog.debug("CATEGORY converter.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 'C':
|
|
|
68bf46 |
+ pc = new ClassNamePatternConverter(formattingInfo, extractPrecisionOption());
|
|
|
68bf46 |
+ // LogLog.debug("CLASS_NAME converter.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 'd':
|
|
|
68bf46 |
+ String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
|
|
|
68bf46 |
+ DateFormat df;
|
|
|
68bf46 |
+ String dOpt = extractOption();
|
|
|
68bf46 |
+ if (dOpt != null)
|
|
|
68bf46 |
+ dateFormatStr = dOpt;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ if (dateFormatStr.equalsIgnoreCase(AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
|
|
|
68bf46 |
+ df = new ISO8601DateFormat();
|
|
|
68bf46 |
+ else if (dateFormatStr.equalsIgnoreCase(AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
|
|
|
68bf46 |
+ df = new AbsoluteTimeDateFormat();
|
|
|
68bf46 |
+ else if (dateFormatStr.equalsIgnoreCase(AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
|
|
|
68bf46 |
+ df = new DateTimeDateFormat();
|
|
|
68bf46 |
+ else {
|
|
|
68bf46 |
+ try {
|
|
|
68bf46 |
+ df = new SimpleDateFormat(dateFormatStr);
|
|
|
68bf46 |
+ } catch (IllegalArgumentException e) {
|
|
|
68bf46 |
+ LogLog.error("Could not instantiate SimpleDateFormat with " + dateFormatStr, e);
|
|
|
68bf46 |
+ df = (DateFormat) OptionConverter.instantiateByClassName(
|
|
|
68bf46 |
+ "org.apache.log4j.helpers.ISO8601DateFormat", DateFormat.class, null);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ pc = new DatePatternConverter(formattingInfo, df);
|
|
|
68bf46 |
+ // LogLog.debug("DATE converter {"+dateFormatStr+"}.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 'F':
|
|
|
68bf46 |
+ pc = new LocationPatternConverter(formattingInfo, FILE_LOCATION_CONVERTER);
|
|
|
68bf46 |
+ // LogLog.debug("File name converter.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 'l':
|
|
|
68bf46 |
+ pc = new LocationPatternConverter(formattingInfo, FULL_LOCATION_CONVERTER);
|
|
|
68bf46 |
+ // LogLog.debug("Location converter.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 'L':
|
|
|
68bf46 |
+ pc = new LocationPatternConverter(formattingInfo, LINE_LOCATION_CONVERTER);
|
|
|
68bf46 |
+ // LogLog.debug("LINE NUMBER converter.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 'm':
|
|
|
68bf46 |
+ pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
|
|
|
68bf46 |
+ // LogLog.debug("MESSAGE converter.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 'M':
|
|
|
68bf46 |
+ pc = new LocationPatternConverter(formattingInfo, METHOD_LOCATION_CONVERTER);
|
|
|
68bf46 |
+ // LogLog.debug("METHOD converter.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 'p':
|
|
|
68bf46 |
+ pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
|
|
|
68bf46 |
+ // LogLog.debug("LEVEL converter.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 'r':
|
|
|
68bf46 |
+ pc = new BasicPatternConverter(formattingInfo, RELATIVE_TIME_CONVERTER);
|
|
|
68bf46 |
+ // LogLog.debug("RELATIVE time converter.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 't':
|
|
|
68bf46 |
+ pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
|
|
|
68bf46 |
+ // LogLog.debug("THREAD converter.");
|
|
|
68bf46 |
+ // formattingInfo.dump();
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ /*
|
|
|
68bf46 |
+ * case 'u': if(i < patternLength) { char cNext = pattern.charAt(i); if(cNext >=
|
|
|
68bf46 |
+ * '0' && cNext <= '9') { pc = new UserFieldPatternConverter(formattingInfo,
|
|
|
68bf46 |
+ * cNext - '0'); LogLog.debug("USER converter ["+cNext+"].");
|
|
|
68bf46 |
+ * formattingInfo.dump(); currentLiteral.setLength(0); i++; } else
|
|
|
68bf46 |
+ * LogLog.error("Unexpected char" +cNext+" at position "+i); } break;
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ case 'x':
|
|
|
68bf46 |
+ pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
|
|
|
68bf46 |
+ // LogLog.debug("NDC converter.");
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ case 'X':
|
|
|
68bf46 |
+ String xOpt = extractOption();
|
|
|
68bf46 |
+ pc = new MDCPatternConverter(formattingInfo, xOpt);
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ break;
|
|
|
68bf46 |
+ default:
|
|
|
68bf46 |
+ LogLog.error("Unexpected char [" + c + "] at position " + i + " in conversion patterrn.");
|
|
|
68bf46 |
+ pc = new LiteralPatternConverter(currentLiteral.toString());
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ addConverter(pc);
|
|
|
68bf46 |
}
|
|
|
68bf46 |
|
|
|
68bf46 |
- public
|
|
|
68bf46 |
- String convert(LoggingEvent event) {
|
|
|
68bf46 |
- switch(type) {
|
|
|
68bf46 |
- case RELATIVE_TIME_CONVERTER:
|
|
|
68bf46 |
- return (Long.toString(event.timeStamp - LoggingEvent.getStartTime()));
|
|
|
68bf46 |
- case THREAD_CONVERTER:
|
|
|
68bf46 |
- return event.getThreadName();
|
|
|
68bf46 |
- case LEVEL_CONVERTER:
|
|
|
68bf46 |
- return event.getLevel().toString();
|
|
|
68bf46 |
- case NDC_CONVERTER:
|
|
|
68bf46 |
- return event.getNDC();
|
|
|
68bf46 |
- case MESSAGE_CONVERTER: {
|
|
|
68bf46 |
- return event.getRenderedMessage();
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- default: return null;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ protected void addConverter(PatternConverter pc) {
|
|
|
68bf46 |
+ currentLiteral.setLength(0);
|
|
|
68bf46 |
+ // Add the pattern converter to the list.
|
|
|
68bf46 |
+ addToList(pc);
|
|
|
68bf46 |
+ // Next pattern is assumed to be a literal.
|
|
|
68bf46 |
+ state = LITERAL_STATE;
|
|
|
68bf46 |
+ // Reset formatting info
|
|
|
68bf46 |
+ formattingInfo.reset();
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
|
|
|
68bf46 |
- private static class LiteralPatternConverter extends PatternConverter {
|
|
|
68bf46 |
- private String literal;
|
|
|
68bf46 |
+ // ---------------------------------------------------------------------
|
|
|
68bf46 |
+ // PatternConverters
|
|
|
68bf46 |
+ // ---------------------------------------------------------------------
|
|
|
68bf46 |
|
|
|
68bf46 |
- LiteralPatternConverter(String value) {
|
|
|
68bf46 |
- literal = value;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ private static class BasicPatternConverter extends PatternConverter {
|
|
|
68bf46 |
+ int type;
|
|
|
68bf46 |
|
|
|
68bf46 |
- public
|
|
|
68bf46 |
- final
|
|
|
68bf46 |
- void format(StringBuffer sbuf, LoggingEvent event) {
|
|
|
68bf46 |
- sbuf.append(literal);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ BasicPatternConverter(FormattingInfo formattingInfo, int type) {
|
|
|
68bf46 |
+ super(formattingInfo);
|
|
|
68bf46 |
+ this.type = type;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public
|
|
|
68bf46 |
- String convert(LoggingEvent event) {
|
|
|
68bf46 |
- return literal;
|
|
|
68bf46 |
+ public String convert(LoggingEvent event) {
|
|
|
68bf46 |
+ switch (type) {
|
|
|
68bf46 |
+ case RELATIVE_TIME_CONVERTER:
|
|
|
68bf46 |
+ return (Long.toString(event.timeStamp - LoggingEvent.getStartTime()));
|
|
|
68bf46 |
+ case THREAD_CONVERTER:
|
|
|
68bf46 |
+ return event.getThreadName();
|
|
|
68bf46 |
+ case LEVEL_CONVERTER:
|
|
|
68bf46 |
+ return event.getLevel().toString();
|
|
|
68bf46 |
+ case NDC_CONVERTER:
|
|
|
68bf46 |
+ return event.getNDC();
|
|
|
68bf46 |
+ case MESSAGE_CONVERTER: {
|
|
|
68bf46 |
+ return event.getRenderedMessage();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ default:
|
|
|
68bf46 |
+ return null;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
|
|
|
68bf46 |
- private static class DatePatternConverter extends PatternConverter {
|
|
|
68bf46 |
- private DateFormat df;
|
|
|
68bf46 |
- private Date date;
|
|
|
68bf46 |
+ private static class LiteralPatternConverter extends PatternConverter {
|
|
|
68bf46 |
+ private String literal;
|
|
|
68bf46 |
|
|
|
68bf46 |
- DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
|
|
|
68bf46 |
- super(formattingInfo);
|
|
|
68bf46 |
- date = new Date();
|
|
|
68bf46 |
- this.df = df;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ LiteralPatternConverter(String value) {
|
|
|
68bf46 |
+ literal = value;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public
|
|
|
68bf46 |
- String convert(LoggingEvent event) {
|
|
|
68bf46 |
- date.setTime(event.timeStamp);
|
|
|
68bf46 |
- String converted = null;
|
|
|
68bf46 |
- try {
|
|
|
68bf46 |
- converted = df.format(date);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- catch (Exception ex) {
|
|
|
68bf46 |
- LogLog.error("Error occured while converting date.", ex);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- return converted;
|
|
|
68bf46 |
+ public final void format(StringBuffer sbuf, LoggingEvent event) {
|
|
|
68bf46 |
+ sbuf.append(literal);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ public String convert(LoggingEvent event) {
|
|
|
68bf46 |
+ return literal;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
|
|
|
68bf46 |
- private static class MDCPatternConverter extends PatternConverter {
|
|
|
68bf46 |
- private String key;
|
|
|
68bf46 |
+ private static class DatePatternConverter extends PatternConverter {
|
|
|
68bf46 |
+ private DateFormat df;
|
|
|
68bf46 |
+ private Date date;
|
|
|
68bf46 |
|
|
|
68bf46 |
- MDCPatternConverter(FormattingInfo formattingInfo, String key) {
|
|
|
68bf46 |
- super(formattingInfo);
|
|
|
68bf46 |
- this.key = key;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
|
|
|
68bf46 |
+ super(formattingInfo);
|
|
|
68bf46 |
+ date = new Date();
|
|
|
68bf46 |
+ this.df = df;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public
|
|
|
68bf46 |
- String convert(LoggingEvent event) {
|
|
|
68bf46 |
- if (key == null) {
|
|
|
68bf46 |
- StringBuffer buf = new StringBuffer("{");
|
|
|
68bf46 |
- Map properties = event.getProperties();
|
|
|
68bf46 |
- if (properties.size() > 0) {
|
|
|
68bf46 |
- Object[] keys = properties.keySet().toArray();
|
|
|
68bf46 |
- Arrays.sort(keys);
|
|
|
68bf46 |
- for (int i = 0; i < keys.length; i++) {
|
|
|
68bf46 |
- buf.append('{');
|
|
|
68bf46 |
- buf.append(keys[i]);
|
|
|
68bf46 |
- buf.append(',');
|
|
|
68bf46 |
- buf.append(properties.get(keys[i]));
|
|
|
68bf46 |
- buf.append('}');
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- buf.append('}');
|
|
|
68bf46 |
- return buf.toString();
|
|
|
68bf46 |
- } else {
|
|
|
68bf46 |
- Object val = event.getMDC(key);
|
|
|
68bf46 |
- if(val == null) {
|
|
|
68bf46 |
- return null;
|
|
|
68bf46 |
- } else {
|
|
|
68bf46 |
- return val.toString();
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ public String convert(LoggingEvent event) {
|
|
|
68bf46 |
+ date.setTime(event.timeStamp);
|
|
|
68bf46 |
+ String converted = null;
|
|
|
68bf46 |
+ try {
|
|
|
68bf46 |
+ converted = df.format(date);
|
|
|
68bf46 |
+ } catch (Exception ex) {
|
|
|
68bf46 |
+ LogLog.error("Error occured while converting date.", ex);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ return converted;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ private static class MDCPatternConverter extends PatternConverter {
|
|
|
68bf46 |
+ private String key;
|
|
|
68bf46 |
|
|
|
68bf46 |
- private class LocationPatternConverter extends PatternConverter {
|
|
|
68bf46 |
- int type;
|
|
|
68bf46 |
+ MDCPatternConverter(FormattingInfo formattingInfo, String key) {
|
|
|
68bf46 |
+ super(formattingInfo);
|
|
|
68bf46 |
+ this.key = key;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- LocationPatternConverter(FormattingInfo formattingInfo, int type) {
|
|
|
68bf46 |
- super(formattingInfo);
|
|
|
68bf46 |
- this.type = type;
|
|
|
68bf46 |
+ public String convert(LoggingEvent event) {
|
|
|
68bf46 |
+ if (key == null) {
|
|
|
68bf46 |
+ StringBuffer buf = new StringBuffer("{");
|
|
|
68bf46 |
+ Map properties = event.getProperties();
|
|
|
68bf46 |
+ if (properties.size() > 0) {
|
|
|
68bf46 |
+ Object[] keys = properties.keySet().toArray();
|
|
|
68bf46 |
+ Arrays.sort(keys);
|
|
|
68bf46 |
+ for (int i = 0; i < keys.length; i++) {
|
|
|
68bf46 |
+ buf.append('{');
|
|
|
68bf46 |
+ buf.append(keys[i]);
|
|
|
68bf46 |
+ buf.append(',');
|
|
|
68bf46 |
+ buf.append(properties.get(keys[i]));
|
|
|
68bf46 |
+ buf.append('}');
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ buf.append('}');
|
|
|
68bf46 |
+ return buf.toString();
|
|
|
68bf46 |
+ } else {
|
|
|
68bf46 |
+ Object val = event.getMDC(key);
|
|
|
68bf46 |
+ if (val == null) {
|
|
|
68bf46 |
+ return null;
|
|
|
68bf46 |
+ } else {
|
|
|
68bf46 |
+ return val.toString();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
|
|
|
68bf46 |
- public
|
|
|
68bf46 |
- String convert(LoggingEvent event) {
|
|
|
68bf46 |
- LocationInfo locationInfo = event.getLocationInformation();
|
|
|
68bf46 |
- switch(type) {
|
|
|
68bf46 |
- case FULL_LOCATION_CONVERTER:
|
|
|
68bf46 |
- return locationInfo.fullInfo;
|
|
|
68bf46 |
- case METHOD_LOCATION_CONVERTER:
|
|
|
68bf46 |
- return locationInfo.getMethodName();
|
|
|
68bf46 |
- case LINE_LOCATION_CONVERTER:
|
|
|
68bf46 |
- return locationInfo.getLineNumber();
|
|
|
68bf46 |
- case FILE_LOCATION_CONVERTER:
|
|
|
68bf46 |
- return locationInfo.getFileName();
|
|
|
68bf46 |
- default: return null;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ private class LocationPatternConverter extends PatternConverter {
|
|
|
68bf46 |
+ int type;
|
|
|
68bf46 |
|
|
|
68bf46 |
- private static abstract class NamedPatternConverter extends PatternConverter {
|
|
|
68bf46 |
- int precision;
|
|
|
68bf46 |
+ LocationPatternConverter(FormattingInfo formattingInfo, int type) {
|
|
|
68bf46 |
+ super(formattingInfo);
|
|
|
68bf46 |
+ this.type = type;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
|
|
|
68bf46 |
- super(formattingInfo);
|
|
|
68bf46 |
- this.precision = precision;
|
|
|
68bf46 |
+ public String convert(LoggingEvent event) {
|
|
|
68bf46 |
+ LocationInfo locationInfo = event.getLocationInformation();
|
|
|
68bf46 |
+ switch (type) {
|
|
|
68bf46 |
+ case FULL_LOCATION_CONVERTER:
|
|
|
68bf46 |
+ return locationInfo.fullInfo;
|
|
|
68bf46 |
+ case METHOD_LOCATION_CONVERTER:
|
|
|
68bf46 |
+ return locationInfo.getMethodName();
|
|
|
68bf46 |
+ case LINE_LOCATION_CONVERTER:
|
|
|
68bf46 |
+ return locationInfo.getLineNumber();
|
|
|
68bf46 |
+ case FILE_LOCATION_CONVERTER:
|
|
|
68bf46 |
+ return locationInfo.getFileName();
|
|
|
68bf46 |
+ default:
|
|
|
68bf46 |
+ return null;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
|
|
|
68bf46 |
- abstract
|
|
|
68bf46 |
- String getFullyQualifiedName(LoggingEvent event);
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- public
|
|
|
68bf46 |
- String convert(LoggingEvent event) {
|
|
|
68bf46 |
- String n = getFullyQualifiedName(event);
|
|
|
68bf46 |
- if(precision <= 0)
|
|
|
68bf46 |
- return n;
|
|
|
68bf46 |
- else {
|
|
|
68bf46 |
- int len = n.length();
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- // We substract 1 from 'len' when assigning to 'end' to avoid out of
|
|
|
68bf46 |
- // bounds exception in return r.substring(end+1, len). This can happen if
|
|
|
68bf46 |
- // precision is 1 and the category name ends with a dot.
|
|
|
68bf46 |
- int end = len -1 ;
|
|
|
68bf46 |
- for(int i = precision; i > 0; i--) {
|
|
|
68bf46 |
- end = n.lastIndexOf('.', end-1);
|
|
|
68bf46 |
- if(end == -1)
|
|
|
68bf46 |
- return n;
|
|
|
68bf46 |
+ private static abstract class NamedPatternConverter extends PatternConverter {
|
|
|
68bf46 |
+ int precision;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
|
|
|
68bf46 |
+ super(formattingInfo);
|
|
|
68bf46 |
+ this.precision = precision;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ abstract String getFullyQualifiedName(LoggingEvent event);
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ public String convert(LoggingEvent event) {
|
|
|
68bf46 |
+ String n = getFullyQualifiedName(event);
|
|
|
68bf46 |
+ if (precision <= 0)
|
|
|
68bf46 |
+ return n;
|
|
|
68bf46 |
+ else {
|
|
|
68bf46 |
+ int len = n.length();
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
|
|
|
68bf46 |
+ // bounds exception in return r.substring(end+1, len). This can happen if
|
|
|
68bf46 |
+ // precision is 1 and the category name ends with a dot.
|
|
|
68bf46 |
+ int end = len - 1;
|
|
|
68bf46 |
+ for (int i = precision; i > 0; i--) {
|
|
|
68bf46 |
+ end = n.lastIndexOf('.', end - 1);
|
|
|
68bf46 |
+ if (end == -1)
|
|
|
68bf46 |
+ return n;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ return n.substring(end + 1, len);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- return n.substring(end+1, len);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
|
|
|
68bf46 |
- private class ClassNamePatternConverter extends NamedPatternConverter {
|
|
|
68bf46 |
+ private class ClassNamePatternConverter extends NamedPatternConverter {
|
|
|
68bf46 |
|
|
|
68bf46 |
- ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
|
|
|
68bf46 |
- super(formattingInfo, precision);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
|
|
|
68bf46 |
+ super(formattingInfo, precision);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- String getFullyQualifiedName(LoggingEvent event) {
|
|
|
68bf46 |
- return event.getLocationInformation().getClassName();
|
|
|
68bf46 |
+ String getFullyQualifiedName(LoggingEvent event) {
|
|
|
68bf46 |
+ return event.getLocationInformation().getClassName();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
|
|
|
68bf46 |
- private class CategoryPatternConverter extends NamedPatternConverter {
|
|
|
68bf46 |
+ private class CategoryPatternConverter extends NamedPatternConverter {
|
|
|
68bf46 |
|
|
|
68bf46 |
- CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
|
|
|
68bf46 |
- super(formattingInfo, precision);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
|
|
|
68bf46 |
+ super(formattingInfo, precision);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- String getFullyQualifiedName(LoggingEvent event) {
|
|
|
68bf46 |
- return event.getLoggerName();
|
|
|
68bf46 |
+ String getFullyQualifiedName(LoggingEvent event) {
|
|
|
68bf46 |
+ return event.getLoggerName();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
-
|
|
|
68bf46 |
diff --git a/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java b/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java
|
|
|
68bf46 |
index ad35f657..6ba9b66c 100644
|
|
|
68bf46 |
--- a/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java
|
|
|
68bf46 |
+++ b/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java
|
|
|
68bf46 |
@@ -18,381 +18,414 @@ package org.apache.log4j.jdbc;
|
|
|
68bf46 |
|
|
|
68bf46 |
import java.sql.Connection;
|
|
|
68bf46 |
import java.sql.DriverManager;
|
|
|
68bf46 |
+import java.sql.PreparedStatement;
|
|
|
68bf46 |
import java.sql.SQLException;
|
|
|
68bf46 |
import java.sql.Statement;
|
|
|
68bf46 |
import java.util.ArrayList;
|
|
|
68bf46 |
-import java.util.Iterator;
|
|
|
68bf46 |
|
|
|
68bf46 |
import org.apache.log4j.PatternLayout;
|
|
|
68bf46 |
+import org.apache.log4j.helpers.LogLog;
|
|
|
68bf46 |
import org.apache.log4j.spi.ErrorCode;
|
|
|
68bf46 |
import org.apache.log4j.spi.LoggingEvent;
|
|
|
68bf46 |
|
|
|
68bf46 |
-
|
|
|
68bf46 |
/**
|
|
|
68bf46 |
- The JDBCAppender provides for sending log events to a database.
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- <font color="#FF2222">WARNING: This version of JDBCAppender
|
|
|
68bf46 |
- is very likely to be completely replaced in the future. Moreoever,
|
|
|
68bf46 |
- it does not log exceptions</font>.
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- Each append call adds to an ArrayList buffer. When
|
|
|
68bf46 |
- the buffer is filled each log event is placed in a sql statement
|
|
|
68bf46 |
- (configurable) and executed.
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- BufferSize, db URL, User, & Password are
|
|
|
68bf46 |
- configurable options in the standard log4j ways.
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- The setSql(String sql) sets the SQL statement to be
|
|
|
68bf46 |
- used for logging -- this statement is sent to a
|
|
|
68bf46 |
- PatternLayout (either created automaticly by the
|
|
|
68bf46 |
- appender or added by the user). Therefore by default all the
|
|
|
68bf46 |
- conversion patterns in PatternLayout can be used
|
|
|
68bf46 |
- inside of the statement. (see the test cases for examples)
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- Overriding the {@link #getLogStatement} method allows more
|
|
|
68bf46 |
- explicit control of the statement used for logging.
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- For use as a base class:
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- Override getConnection() to pass any connection
|
|
|
68bf46 |
- you want. Typically this is used to enable application wide
|
|
|
68bf46 |
- connection pooling.
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- Override closeConnection(Connection con) -- if
|
|
|
68bf46 |
- you override getConnection make sure to implement
|
|
|
68bf46 |
- closeConnection to handle the connection you
|
|
|
68bf46 |
- generated. Typically this would return the connection to the
|
|
|
68bf46 |
- pool it came from.
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- Override getLogStatement(LoggingEvent event) to
|
|
|
68bf46 |
- produce specialized or dynamic statements. The default uses the
|
|
|
68bf46 |
- sql option value.
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- @author Kevin Steppe (ksteppe@pacbell.net)
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-*/
|
|
|
68bf46 |
-public class JDBCAppender extends org.apache.log4j.AppenderSkeleton
|
|
|
68bf46 |
- implements org.apache.log4j.Appender {
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * URL of the DB for default connection handling
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected String databaseURL = "jdbc:odbc:myDB";
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * User to connect as for default connection handling
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected String databaseUser = "me";
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * User to use for default connection handling
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected String databasePassword = "mypassword";
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * Connection used by default. The connection is opened the first time it
|
|
|
68bf46 |
- * is needed and then held open until the appender is closed (usually at
|
|
|
68bf46 |
- * garbage collection). This behavior is best modified by creating a
|
|
|
68bf46 |
- * sub-class and overriding the getConnection and
|
|
|
68bf46 |
- * closeConnection methods.
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected Connection connection = null;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * Stores the string given to the pattern layout for conversion into a SQL
|
|
|
68bf46 |
- * statement, eg: insert into LogTable (Thread, Class, Message) values
|
|
|
68bf46 |
- * ("%t", "%c", "%m").
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- * Be careful of quotes in your messages!
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- * Also see PatternLayout.
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected String sqlStatement = "";
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * size of LoggingEvent buffer before writting to the database.
|
|
|
68bf46 |
- * Default is 1.
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected int bufferSize = 1;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * ArrayList holding the buffer of Logging Events.
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected ArrayList buffer;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * Helper object for clearing out the buffer
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected ArrayList removes;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- private boolean locationInfo = false;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- public JDBCAppender() {
|
|
|
68bf46 |
- super();
|
|
|
68bf46 |
- buffer = new ArrayList(bufferSize);
|
|
|
68bf46 |
- removes = new ArrayList(bufferSize);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * Gets whether the location of the logging request call
|
|
|
68bf46 |
- * should be captured.
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- * @since 1.2.16
|
|
|
68bf46 |
- * @return the current value of the LocationInfo option.
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- public boolean getLocationInfo() {
|
|
|
68bf46 |
- return locationInfo;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * The LocationInfo option takes a boolean value. By default, it is
|
|
|
68bf46 |
- * set to false which means there will be no effort to extract the location
|
|
|
68bf46 |
- * information related to the event. As a result, the event that will be
|
|
|
68bf46 |
- * ultimately logged will likely to contain the wrong location information
|
|
|
68bf46 |
- * (if present in the log format).
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- * Location information extraction is comparatively very slow and should be
|
|
|
68bf46 |
- * avoided unless performance is not a concern.
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- * @since 1.2.16
|
|
|
68bf46 |
- * @param flag true if location information should be extracted.
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- public void setLocationInfo(final boolean flag) {
|
|
|
68bf46 |
- locationInfo = flag;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * Adds the event to the buffer. When full the buffer is flushed.
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- public void append(LoggingEvent event) {
|
|
|
68bf46 |
- event.getNDC();
|
|
|
68bf46 |
- event.getThreadName();
|
|
|
68bf46 |
- // Get a copy of this thread's MDC.
|
|
|
68bf46 |
- event.getMDCCopy();
|
|
|
68bf46 |
- if (locationInfo) {
|
|
|
68bf46 |
- event.getLocationInformation();
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- event.getRenderedMessage();
|
|
|
68bf46 |
- event.getThrowableStrRep();
|
|
|
68bf46 |
- buffer.add(event);
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- if (buffer.size() >= bufferSize)
|
|
|
68bf46 |
- flushBuffer();
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * By default getLogStatement sends the event to the required Layout object.
|
|
|
68bf46 |
- * The layout will format the given pattern into a workable SQL string.
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- * Overriding this provides direct access to the LoggingEvent
|
|
|
68bf46 |
- * when constructing the logging statement.
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected String getLogStatement(LoggingEvent event) {
|
|
|
68bf46 |
- return getLayout().format(event);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- * Override this to provide an alertnate method of getting
|
|
|
68bf46 |
- * connections (such as caching). One method to fix this is to open
|
|
|
68bf46 |
- * connections at the start of flushBuffer() and close them at the
|
|
|
68bf46 |
- * end. I use a connection pool outside of JDBCAppender which is
|
|
|
68bf46 |
- * accessed in an override of this method.
|
|
|
68bf46 |
- * */
|
|
|
68bf46 |
- protected void execute(String sql) throws SQLException {
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- Connection con = null;
|
|
|
68bf46 |
- Statement stmt = null;
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- try {
|
|
|
68bf46 |
- con = getConnection();
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- stmt = con.createStatement();
|
|
|
68bf46 |
- stmt.executeUpdate(sql);
|
|
|
68bf46 |
- } finally {
|
|
|
68bf46 |
- if(stmt != null) {
|
|
|
68bf46 |
- stmt.close();
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- closeConnection(con);
|
|
|
68bf46 |
+ * The JDBCAppender provides for sending log events to a database.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * <font color="#FF2222">WARNING: This version of JDBCAppender is very likely
|
|
|
68bf46 |
+ * to be completely replaced in the future. Moreoever, it does not log
|
|
|
68bf46 |
+ * exceptions</font>.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Each append call adds to an ArrayList buffer. When the buffer is
|
|
|
68bf46 |
+ * filled each log event is placed in a sql statement (configurable) and
|
|
|
68bf46 |
+ * executed.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * BufferSize, db URL, User, & Password are
|
|
|
68bf46 |
+ * configurable options in the standard log4j ways.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * The setSql(String sql) sets the SQL statement to be used for
|
|
|
68bf46 |
+ * logging -- this statement is sent to a PatternLayout (either
|
|
|
68bf46 |
+ * created automatically by the appender or added by the user). Therefore by
|
|
|
68bf46 |
+ * default all the conversion patterns in PatternLayout can be used
|
|
|
68bf46 |
+ * inside of the statement. (see the test cases for examples)
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Overriding the {@link #getLogStatement} method allows more explicit control
|
|
|
68bf46 |
+ * of the statement used for logging.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * For use as a base class:
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Override getConnection() to pass any connection you want.
|
|
|
68bf46 |
+ * Typically this is used to enable application wide connection pooling.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Override closeConnection(Connection con) -- if you override
|
|
|
68bf46 |
+ * getConnection make sure to implement closeConnection to handle
|
|
|
68bf46 |
+ * the connection you generated. Typically this would return the connection to
|
|
|
68bf46 |
+ * the pool it came from.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Override getLogStatement(LoggingEvent event) to produce
|
|
|
68bf46 |
+ * specialized or dynamic statements. The default uses the sql option value.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * @author Kevin Steppe
|
|
|
68bf46 |
+ * (ksteppe@pacbell.net)
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+public class JDBCAppender extends org.apache.log4j.AppenderSkeleton implements org.apache.log4j.Appender {
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+// private final Boolean secureSqlReplacement = Boolean
|
|
|
68bf46 |
+// .parseBoolean(System.getProperty("org.apache.log4j.jdbc.JDBCAppender.secure_jdbc_replacement", "true"));
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private static final IllegalArgumentException ILLEGAL_PATTERN_FOR_SECURE_EXEC = new IllegalArgumentException(
|
|
|
68bf46 |
+ "Only org.apache.log4j.PatternLayout is supported for now due to CVE-2022-23305");
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * URL of the DB for default connection handling
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected String databaseURL = "jdbc:odbc:myDB";
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * User to connect as for default connection handling
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected String databaseUser = "me";
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * User to use for default connection handling
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected String databasePassword = "mypassword";
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * Connection used by default. The connection is opened the first time it is
|
|
|
68bf46 |
+ * needed and then held open until the appender is closed (usually at garbage
|
|
|
68bf46 |
+ * collection). This behavior is best modified by creating a sub-class and
|
|
|
68bf46 |
+ * overriding the getConnection and closeConnection
|
|
|
68bf46 |
+ * methods.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected Connection connection = null;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * Stores the string given to the pattern layout for conversion into a SQL
|
|
|
68bf46 |
+ * statement, eg: insert into LogTable (Thread, Class, Message) values ("%t",
|
|
|
68bf46 |
+ * "%c", "%m").
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Be careful of quotes in your messages!
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Also see PatternLayout.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected String sqlStatement = "";
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private JdbcPatternParser preparedStatementParser;
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * size of LoggingEvent buffer before writting to the database. Default is 1.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected int bufferSize = 1;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * ArrayList holding the buffer of Logging Events.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected ArrayList<LoggingEvent> buffer;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private boolean locationInfo = false;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private boolean isActive = false;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ public JDBCAppender() {
|
|
|
68bf46 |
+ super();
|
|
|
68bf46 |
+ buffer = new ArrayList<LoggingEvent>(bufferSize);
|
|
|
68bf46 |
}
|
|
|
68bf46 |
|
|
|
68bf46 |
- //System.out.println("Execute: " + sql);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * Override this to return the connection to a pool, or to clean up the
|
|
|
68bf46 |
- * resource.
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- * The default behavior holds a single connection open until the appender
|
|
|
68bf46 |
- * is closed (typically when garbage collected).
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected void closeConnection(Connection con) {
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * Override this to link with your connection pooling system.
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- * By default this creates a single connection which is held open
|
|
|
68bf46 |
- * until the object is garbage collected.
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- protected Connection getConnection() throws SQLException {
|
|
|
68bf46 |
- if (!DriverManager.getDrivers().hasMoreElements())
|
|
|
68bf46 |
- setDriver("sun.jdbc.odbc.JdbcOdbcDriver");
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- if (connection == null) {
|
|
|
68bf46 |
- connection = DriverManager.getConnection(databaseURL, databaseUser,
|
|
|
68bf46 |
- databasePassword);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- return connection;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * Closes the appender, flushing the buffer first then closing the default
|
|
|
68bf46 |
- * connection if it is open.
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- public void close()
|
|
|
68bf46 |
- {
|
|
|
68bf46 |
- flushBuffer();
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- try {
|
|
|
68bf46 |
- if (connection != null && !connection.isClosed())
|
|
|
68bf46 |
- connection.close();
|
|
|
68bf46 |
- } catch (SQLException e) {
|
|
|
68bf46 |
- errorHandler.error("Error closing connection", e, ErrorCode.GENERIC_FAILURE);
|
|
|
68bf46 |
+ @Override
|
|
|
68bf46 |
+ public void activateOptions() {
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ if (getSql() == null || getSql().trim().length() == 0) {
|
|
|
68bf46 |
+ LogLog.error("JDBCAppender.sql parameter is mandatory. Skipping all futher processing");
|
|
|
68bf46 |
+ isActive = false;
|
|
|
68bf46 |
+ return;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ LogLog.debug("JDBCAppender constructing internal pattern parser");
|
|
|
68bf46 |
+ preparedStatementParser = new JdbcPatternParser(getSql());
|
|
|
68bf46 |
+ isActive = true;
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- this.closed = true;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * loops through the buffer of LoggingEvents, gets a
|
|
|
68bf46 |
- * sql string from getLogStatement() and sends it to execute().
|
|
|
68bf46 |
- * Errors are sent to the errorHandler.
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- * If a statement fails the LoggingEvent stays in the buffer!
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- public void flushBuffer() {
|
|
|
68bf46 |
- //Do the actual logging
|
|
|
68bf46 |
- removes.ensureCapacity(buffer.size());
|
|
|
68bf46 |
- for (Iterator i = buffer.iterator(); i.hasNext();) {
|
|
|
68bf46 |
- LoggingEvent logEvent = (LoggingEvent)i.next();
|
|
|
68bf46 |
- try {
|
|
|
68bf46 |
- String sql = getLogStatement(logEvent);
|
|
|
68bf46 |
- execute(sql);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
- catch (SQLException e) {
|
|
|
68bf46 |
- errorHandler.error("Failed to excute sql", e,
|
|
|
68bf46 |
- ErrorCode.FLUSH_FAILURE);
|
|
|
68bf46 |
- } finally {
|
|
|
68bf46 |
- removes.add(logEvent);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * Gets whether the location of the logging request call should be captured.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * @since 1.2.16
|
|
|
68bf46 |
+ * @return the current value of the LocationInfo option.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ public boolean getLocationInfo() {
|
|
|
68bf46 |
+ return locationInfo;
|
|
|
68bf46 |
}
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- // remove from the buffer any events that were reported
|
|
|
68bf46 |
- buffer.removeAll(removes);
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- // clear the buffer of reported events
|
|
|
68bf46 |
- removes.clear();
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /** closes the appender before disposal */
|
|
|
68bf46 |
- public void finalize() {
|
|
|
68bf46 |
- close();
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * JDBCAppender requires a layout.
|
|
|
68bf46 |
- * */
|
|
|
68bf46 |
- public boolean requiresLayout() {
|
|
|
68bf46 |
- return true;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
-
|
|
|
68bf46 |
-
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- *
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- public void setSql(String s) {
|
|
|
68bf46 |
- sqlStatement = s;
|
|
|
68bf46 |
- if (getLayout() == null) {
|
|
|
68bf46 |
- this.setLayout(new PatternLayout(s));
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * The LocationInfo option takes a boolean value. By default, it is set
|
|
|
68bf46 |
+ * to false which means there will be no effort to extract the location
|
|
|
68bf46 |
+ * information related to the event. As a result, the event that will be
|
|
|
68bf46 |
+ * ultimately logged will likely to contain the wrong location information (if
|
|
|
68bf46 |
+ * present in the log format).
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Location information extraction is comparatively very slow and should be
|
|
|
68bf46 |
+ * avoided unless performance is not a concern.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * @since 1.2.16
|
|
|
68bf46 |
+ * @param flag true if location information should be extracted.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ public void setLocationInfo(final boolean flag) {
|
|
|
68bf46 |
+ locationInfo = flag;
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- else {
|
|
|
68bf46 |
- ((PatternLayout)getLayout()).setConversionPattern(s);
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * Adds the event to the buffer. When full the buffer is flushed.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ public void append(LoggingEvent event) {
|
|
|
68bf46 |
+ if (!isActive) {
|
|
|
68bf46 |
+ return;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ event.getNDC();
|
|
|
68bf46 |
+ event.getThreadName();
|
|
|
68bf46 |
+ // Get a copy of this thread's MDC.
|
|
|
68bf46 |
+ event.getMDCCopy();
|
|
|
68bf46 |
+ if (locationInfo) {
|
|
|
68bf46 |
+ event.getLocationInformation();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ event.getRenderedMessage();
|
|
|
68bf46 |
+ event.getThrowableStrRep();
|
|
|
68bf46 |
+ buffer.add(event);
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ if (buffer.size() >= bufferSize)
|
|
|
68bf46 |
+ flushBuffer();
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * By default getLogStatement sends the event to the required Layout object. The
|
|
|
68bf46 |
+ * layout will format the given pattern into a workable SQL string.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Overriding this provides direct access to the LoggingEvent when constructing
|
|
|
68bf46 |
+ * the logging statement.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected String getLogStatement(LoggingEvent event) {
|
|
|
68bf46 |
+ return getLayout().format(event);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * Returns pre-formated statement eg: insert into LogTable (msg) values ("%m")
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- public String getSql() {
|
|
|
68bf46 |
- return sqlStatement;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Override this to provide an alertnate method of getting connections (such as
|
|
|
68bf46 |
+ * caching). One method to fix this is to open connections at the start of
|
|
|
68bf46 |
+ * flushBuffer() and close them at the end. I use a connection pool outside of
|
|
|
68bf46 |
+ * JDBCAppender which is accessed in an override of this method.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected void execute(String sql) throws SQLException {
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ Connection con = null;
|
|
|
68bf46 |
+ Statement stmt = null;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ try {
|
|
|
68bf46 |
+ con = getConnection();
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ stmt = con.createStatement();
|
|
|
68bf46 |
+ stmt.executeUpdate(sql);
|
|
|
68bf46 |
+ } finally {
|
|
|
68bf46 |
+ if (stmt != null) {
|
|
|
68bf46 |
+ stmt.close();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ closeConnection(con);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ // System.out.println("Execute: " + sql);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * Override this to return the connection to a pool, or to clean up the
|
|
|
68bf46 |
+ * resource.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * The default behavior holds a single connection open until the appender is
|
|
|
68bf46 |
+ * closed (typically when garbage collected).
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected void closeConnection(Connection con) {
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public void setUser(String user) {
|
|
|
68bf46 |
- databaseUser = user;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * Override this to link with your connection pooling system.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * By default this creates a single connection which is held open until the
|
|
|
68bf46 |
+ * object is garbage collected.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ protected Connection getConnection() throws SQLException {
|
|
|
68bf46 |
+ if (!DriverManager.getDrivers().hasMoreElements())
|
|
|
68bf46 |
+ setDriver("sun.jdbc.odbc.JdbcOdbcDriver");
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ if (connection == null) {
|
|
|
68bf46 |
+ connection = DriverManager.getConnection(databaseURL, databaseUser, databasePassword);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ return connection;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * Closes the appender, flushing the buffer first then closing the default
|
|
|
68bf46 |
+ * connection if it is open.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ public void close() {
|
|
|
68bf46 |
+ flushBuffer();
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ try {
|
|
|
68bf46 |
+ if (connection != null && !connection.isClosed())
|
|
|
68bf46 |
+ connection.close();
|
|
|
68bf46 |
+ } catch (SQLException e) {
|
|
|
68bf46 |
+ errorHandler.error("Error closing connection", e, ErrorCode.GENERIC_FAILURE);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ this.closed = true;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public void setURL(String url) {
|
|
|
68bf46 |
- databaseURL = url;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * loops through the buffer of LoggingEvents, gets a sql string from
|
|
|
68bf46 |
+ * getLogStatement() and sends it to execute(). Errors are sent to the
|
|
|
68bf46 |
+ * errorHandler.
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * If a statement fails the LoggingEvent stays in the buffer!
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ public void flushBuffer() {
|
|
|
68bf46 |
+ if (buffer.isEmpty()) {
|
|
|
68bf46 |
+ return;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ flushBufferSecure();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ private void flushBufferSecure() {
|
|
|
68bf46 |
+ // Prepare events that we will store to the DB
|
|
|
68bf46 |
+ ArrayList<LoggingEvent> removes = new ArrayList<LoggingEvent>(buffer);
|
|
|
68bf46 |
+ buffer.removeAll(removes);
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ if (layout.getClass() != PatternLayout.class) {
|
|
|
68bf46 |
+ errorHandler.error("Failed to convert pattern " + layout + " to SQL", ILLEGAL_PATTERN_FOR_SECURE_EXEC,
|
|
|
68bf46 |
+ ErrorCode.MISSING_LAYOUT);
|
|
|
68bf46 |
+ return;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ Connection con = null;
|
|
|
68bf46 |
+ boolean useBatch = removes.size() > 1;
|
|
|
68bf46 |
+ try {
|
|
|
68bf46 |
+ con = getConnection();
|
|
|
68bf46 |
+ PreparedStatement ps = con.prepareStatement(preparedStatementParser.getParameterizedSql());
|
|
|
68bf46 |
+ safelyInsertIntoDB(removes, useBatch, ps);
|
|
|
68bf46 |
+ } catch (SQLException e) {
|
|
|
68bf46 |
+ errorHandler.error("Failed to append messages sql", e, ErrorCode.FLUSH_FAILURE);
|
|
|
68bf46 |
+ } finally {
|
|
|
68bf46 |
+ if (con != null) {
|
|
|
68bf46 |
+ closeConnection(con);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private void safelyInsertIntoDB(ArrayList<LoggingEvent> removes, boolean useBatch, PreparedStatement ps)
|
|
|
68bf46 |
+ throws SQLException {
|
|
|
68bf46 |
+ try {
|
|
|
68bf46 |
+ for (LoggingEvent logEvent : removes) {
|
|
|
68bf46 |
+ try {
|
|
|
68bf46 |
+ preparedStatementParser.setParameters(ps, logEvent);
|
|
|
68bf46 |
+ if (useBatch) {
|
|
|
68bf46 |
+ ps.addBatch();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ } catch (SQLException e) {
|
|
|
68bf46 |
+ errorHandler.error("Failed to append parameters", e, ErrorCode.FLUSH_FAILURE);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ if (useBatch) {
|
|
|
68bf46 |
+ ps.executeBatch();
|
|
|
68bf46 |
+ } else {
|
|
|
68bf46 |
+ ps.execute();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ } finally {
|
|
|
68bf46 |
+ try {
|
|
|
68bf46 |
+ ps.close();
|
|
|
68bf46 |
+ } catch (SQLException ignored) {
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public void setPassword(String password) {
|
|
|
68bf46 |
- databasePassword = password;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ /** closes the appender before disposal */
|
|
|
68bf46 |
+ public void finalize() {
|
|
|
68bf46 |
+ close();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * JDBCAppender requires a layout.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ public boolean requiresLayout() {
|
|
|
68bf46 |
+ return true;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public void setBufferSize(int newBufferSize) {
|
|
|
68bf46 |
- bufferSize = newBufferSize;
|
|
|
68bf46 |
- buffer.ensureCapacity(bufferSize);
|
|
|
68bf46 |
- removes.ensureCapacity(bufferSize);
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ public void setSql(String s) {
|
|
|
68bf46 |
+ sqlStatement = s;
|
|
|
68bf46 |
+ if (getLayout() == null) {
|
|
|
68bf46 |
+ this.setLayout(new PatternLayout(s));
|
|
|
68bf46 |
+ } else {
|
|
|
68bf46 |
+ ((PatternLayout) getLayout()).setConversionPattern(s);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * Returns pre-formated statement eg: insert into LogTable (msg) values ("%m")
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ public String getSql() {
|
|
|
68bf46 |
+ return sqlStatement;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public String getUser() {
|
|
|
68bf46 |
- return databaseUser;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ public void setUser(String user) {
|
|
|
68bf46 |
+ databaseUser = user;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ public void setURL(String url) {
|
|
|
68bf46 |
+ databaseURL = url;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public String getURL() {
|
|
|
68bf46 |
- return databaseURL;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ public void setPassword(String password) {
|
|
|
68bf46 |
+ databasePassword = password;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ public void setBufferSize(int newBufferSize) {
|
|
|
68bf46 |
+ bufferSize = newBufferSize;
|
|
|
68bf46 |
+ buffer.ensureCapacity(bufferSize);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public String getPassword() {
|
|
|
68bf46 |
- return databasePassword;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ public String getUser() {
|
|
|
68bf46 |
+ return databaseUser;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ public String getURL() {
|
|
|
68bf46 |
+ return databaseURL;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- public int getBufferSize() {
|
|
|
68bf46 |
- return bufferSize;
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
+ public String getPassword() {
|
|
|
68bf46 |
+ return databasePassword;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
+ public int getBufferSize() {
|
|
|
68bf46 |
+ return bufferSize;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
|
|
|
68bf46 |
- /**
|
|
|
68bf46 |
- * Ensures that the given driver class has been loaded for sql connection
|
|
|
68bf46 |
- * creation.
|
|
|
68bf46 |
- */
|
|
|
68bf46 |
- public void setDriver(String driverClass) {
|
|
|
68bf46 |
- try {
|
|
|
68bf46 |
- Class.forName(driverClass);
|
|
|
68bf46 |
- } catch (Exception e) {
|
|
|
68bf46 |
- errorHandler.error("Failed to load driver", e,
|
|
|
68bf46 |
- ErrorCode.GENERIC_FAILURE);
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * Ensures that the given driver class has been loaded for sql connection
|
|
|
68bf46 |
+ * creation.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ public void setDriver(String driverClass) {
|
|
|
68bf46 |
+ try {
|
|
|
68bf46 |
+ Class.forName(driverClass);
|
|
|
68bf46 |
+ } catch (Exception e) {
|
|
|
68bf46 |
+ errorHandler.error("Failed to load driver", e, ErrorCode.GENERIC_FAILURE);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
- }
|
|
|
68bf46 |
}
|
|
|
68bf46 |
-
|
|
|
68bf46 |
diff --git a/src/main/java/org/apache/log4j/jdbc/JdbcPatternParser.java b/src/main/java/org/apache/log4j/jdbc/JdbcPatternParser.java
|
|
|
68bf46 |
new file mode 100644
|
|
|
68bf46 |
index 00000000..d6807dee
|
|
|
68bf46 |
--- /dev/null
|
|
|
68bf46 |
+++ b/src/main/java/org/apache/log4j/jdbc/JdbcPatternParser.java
|
|
|
68bf46 |
@@ -0,0 +1,115 @@
|
|
|
68bf46 |
+/*
|
|
|
68bf46 |
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
68bf46 |
+ * contributor license agreements. See the NOTICE file distributed with
|
|
|
68bf46 |
+ * this work for additional information regarding copyright ownership.
|
|
|
68bf46 |
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
68bf46 |
+ * (the "License"); you may not use this file except in compliance with
|
|
|
68bf46 |
+ * the License. You may obtain a copy of the License at
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
68bf46 |
+ *
|
|
|
68bf46 |
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
68bf46 |
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
68bf46 |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
68bf46 |
+ * See the License for the specific language governing permissions and
|
|
|
68bf46 |
+ * limitations under the License.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+package org.apache.log4j.jdbc;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+import org.apache.log4j.helpers.PatternConverter;
|
|
|
68bf46 |
+import org.apache.log4j.helpers.PatternParser;
|
|
|
68bf46 |
+import org.apache.log4j.spi.LoggingEvent;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+import java.sql.PreparedStatement;
|
|
|
68bf46 |
+import java.sql.SQLException;
|
|
|
68bf46 |
+import java.util.ArrayList;
|
|
|
68bf46 |
+import java.util.Collections;
|
|
|
68bf46 |
+import java.util.List;
|
|
|
68bf46 |
+import java.util.regex.Matcher;
|
|
|
68bf46 |
+import java.util.regex.Pattern;
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+class JdbcPatternParser {
|
|
|
68bf46 |
+ private static final String QUESTION_MARK = "?";
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private static final char PERCENT_CHAR = '%';
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private final static Pattern STRING_LITERAL_PATTERN = Pattern.compile("'((?>[^']|'')+)'");
|
|
|
68bf46 |
+ // NOTE: capturing group work seem to work just as well.
|
|
|
68bf46 |
+ //private final static Pattern STRING_LITERAL_PATTERN = Pattern.compile("'(([^']|'')+)'");
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private String parameterizedSql;
|
|
|
68bf46 |
+ final private List<String> patternStringRepresentationList = new ArrayList<String>();
|
|
|
68bf46 |
+ final private List<PatternConverter> args = new ArrayList<PatternConverter>();
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ JdbcPatternParser(String insertString) {
|
|
|
68bf46 |
+ init(insertString);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ public String getParameterizedSql() {
|
|
|
68bf46 |
+ return parameterizedSql;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ public List<String> getUnmodifiablePatternStringRepresentationList() {
|
|
|
68bf46 |
+ return Collections.unmodifiableList(patternStringRepresentationList);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ @Override
|
|
|
68bf46 |
+ public String toString() {
|
|
|
68bf46 |
+ return "JdbcPatternParser{sql=" + parameterizedSql + ",args=" + patternStringRepresentationList + "}";
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ /**
|
|
|
68bf46 |
+ * Converts '....' literals into bind variables in JDBC.
|
|
|
68bf46 |
+ */
|
|
|
68bf46 |
+ private void init(String insertString) {
|
|
|
68bf46 |
+ if (insertString == null) {
|
|
|
68bf46 |
+ throw new IllegalArgumentException("Null pattern");
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ Matcher m = STRING_LITERAL_PATTERN.matcher(insertString);
|
|
|
68bf46 |
+ StringBuffer sb = new StringBuffer();
|
|
|
68bf46 |
+ while (m.find()) {
|
|
|
68bf46 |
+ String matchedStr = m.group(1);
|
|
|
68bf46 |
+ if (matchedStr.indexOf(PERCENT_CHAR) == -1) {
|
|
|
68bf46 |
+ replaceWithMatchedStr(m, sb);
|
|
|
68bf46 |
+ } else {
|
|
|
68bf46 |
+ // Replace with bind
|
|
|
68bf46 |
+ replaceWithBind(m, sb, matchedStr);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ m.appendTail(sb);
|
|
|
68bf46 |
+ this.parameterizedSql = sb.toString();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private void replaceWithMatchedStr(Matcher m, StringBuffer sb) {
|
|
|
68bf46 |
+ // Just literal, append it as is
|
|
|
68bf46 |
+ m.appendReplacement(sb, "'$1'");
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private void replaceWithBind(Matcher m, StringBuffer sb, String matchedStr) {
|
|
|
68bf46 |
+ m.appendReplacement(sb, QUESTION_MARK);
|
|
|
68bf46 |
+ // We will use prepared statements, so we don't need to escape quotes.
|
|
|
68bf46 |
+ // And we assume the users had 'That''s a string with quotes' in their configs.
|
|
|
68bf46 |
+ matchedStr = matchedStr.replaceAll("''", "'");
|
|
|
68bf46 |
+ patternStringRepresentationList.add(matchedStr);
|
|
|
68bf46 |
+ args.add(new PatternParser(matchedStr).parse());
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ public void setParameters(PreparedStatement ps, LoggingEvent logEvent) throws SQLException {
|
|
|
68bf46 |
+ for (int i = 0; i < args.size(); i++) {
|
|
|
68bf46 |
+ final PatternConverter head = args.get(i);
|
|
|
68bf46 |
+ String value = buildValueStr(logEvent, head);
|
|
|
68bf46 |
+ ps.setString(i + 1, value);
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+
|
|
|
68bf46 |
+ private String buildValueStr(LoggingEvent logEvent, final PatternConverter head) {
|
|
|
68bf46 |
+ StringBuffer buffer = new StringBuffer();
|
|
|
68bf46 |
+ PatternConverter c = head;
|
|
|
68bf46 |
+ while (c != null) {
|
|
|
68bf46 |
+ c.format(buffer, logEvent);
|
|
|
68bf46 |
+ c = c.next;
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+ return buffer.toString();
|
|
|
68bf46 |
+ }
|
|
|
68bf46 |
+}
|
|
|
68bf46 |
--
|
|
|
68bf46 |
2.33.1
|
|
|
68bf46 |
|