From 91770c9312101791a4caa29bf20368fc42687548 Mon Sep 17 00:00:00 2001 From: Rik Veenboer Date: Fri, 3 Jun 2011 15:56:41 +0000 Subject: [PATCH] Log4J source files uit trunk gehaald en vervangen door jar. --- java/lib/log4j-1.2.16.jar | Bin 0 -> 481534 bytes java/src/org/apache/log4j/Appender.java | 142 -- .../org/apache/log4j/AppenderSkeleton.java | 304 --- java/src/org/apache/log4j/AsyncAppender.java | 596 ------ .../org/apache/log4j/BasicConfigurator.java | 73 - java/src/org/apache/log4j/Category.java | 1062 ----------- java/src/org/apache/log4j/CategoryKey.java | 52 - .../src/org/apache/log4j/ConsoleAppender.java | 220 --- .../log4j/DailyRollingFileAppender.java | 454 ----- .../apache/log4j/DefaultCategoryFactory.java | 31 - .../log4j/DefaultThrowableRenderer.java | 83 - java/src/org/apache/log4j/Dispatcher.java | 125 -- .../apache/log4j/EnhancedPatternLayout.java | 559 ------ .../log4j/EnhancedThrowableRenderer.java | 168 -- java/src/org/apache/log4j/FileAppender.java | 348 ---- java/src/org/apache/log4j/HTMLLayout.java | 267 --- java/src/org/apache/log4j/Hierarchy.java | 577 ------ java/src/org/apache/log4j/Layout.java | 89 - java/src/org/apache/log4j/Level.java | 224 --- java/src/org/apache/log4j/LogMF.java | 1677 ----------------- java/src/org/apache/log4j/LogManager.java | 276 --- java/src/org/apache/log4j/LogSF.java | 1541 --------------- java/src/org/apache/log4j/LogXF.java | 371 ---- java/src/org/apache/log4j/Logger.java | 212 --- java/src/org/apache/log4j/MDC.java | 187 -- java/src/org/apache/log4j/NDC.java | 436 ----- java/src/org/apache/log4j/PatternLayout.java | 511 ----- java/src/org/apache/log4j/Priority.java | 193 -- .../apache/log4j/PropertyConfigurator.java | 963 ---------- java/src/org/apache/log4j/ProvisionNode.java | 29 - .../org/apache/log4j/RollingFileAppender.java | 284 --- java/src/org/apache/log4j/SimpleLayout.java | 78 - java/src/org/apache/log4j/TTCCLayout.java | 217 --- java/src/org/apache/log4j/WriterAppender.java | 387 ---- .../apache/log4j/chainsaw/ControlPanel.java | 222 --- .../apache/log4j/chainsaw/DetailPanel.java | 170 -- .../apache/log4j/chainsaw/EventDetails.java | 135 -- .../org/apache/log4j/chainsaw/ExitAction.java | 48 - .../apache/log4j/chainsaw/LoadXMLAction.java | 139 -- .../log4j/chainsaw/LoggingReceiver.java | 121 -- java/src/org/apache/log4j/chainsaw/Main.java | 192 -- .../apache/log4j/chainsaw/MyTableModel.java | 390 ---- .../apache/log4j/chainsaw/XMLFileHandler.java | 170 -- .../log4j/chainsaw/doc-files/screen_01.png | Bin 23150 -> 0 bytes .../org/apache/log4j/chainsaw/package.html | 118 -- .../apache/log4j/config/PropertyGetter.java | 108 -- .../apache/log4j/config/PropertyPrinter.java | 168 -- .../apache/log4j/config/PropertySetter.java | 310 --- .../log4j/config/PropertySetterException.java | 54 - java/src/org/apache/log4j/config/package.html | 23 - .../log4j/helpers/AbsoluteTimeDateFormat.java | 145 -- .../log4j/helpers/AppenderAttachableImpl.java | 176 -- .../org/apache/log4j/helpers/BoundedFIFO.java | 181 -- .../log4j/helpers/CountingQuietWriter.java | 63 - .../apache/log4j/helpers/CyclicBuffer.java | 159 -- .../org/apache/log4j/helpers/DateLayout.java | 200 -- .../log4j/helpers/DateTimeDateFormat.java | 85 - .../apache/log4j/helpers/FileWatchdog.java | 111 -- .../apache/log4j/helpers/FormattingInfo.java | 45 - .../log4j/helpers/ISO8601DateFormat.java | 155 -- java/src/org/apache/log4j/helpers/Loader.java | 200 -- java/src/org/apache/log4j/helpers/LogLog.java | 189 -- .../log4j/helpers/MDCKeySetExtractor.java | 92 - .../apache/log4j/helpers/NullEnumeration.java | 50 - .../log4j/helpers/OnlyOnceErrorHandler.java | 114 -- .../apache/log4j/helpers/OptionConverter.java | 485 ----- .../log4j/helpers/PatternConverter.java | 111 -- .../apache/log4j/helpers/PatternParser.java | 570 ------ .../org/apache/log4j/helpers/QuietWriter.java | 76 - .../log4j/helpers/RelativeTimeDateFormat.java | 65 - .../log4j/helpers/SyslogQuietWriter.java | 56 - .../apache/log4j/helpers/SyslogWriter.java | 145 -- .../apache/log4j/helpers/ThreadLocalMap.java | 42 - .../org/apache/log4j/helpers/Transform.java | 113 -- .../src/org/apache/log4j/helpers/package.html | 33 - .../org/apache/log4j/jdbc/JDBCAppender.java | 398 ---- java/src/org/apache/log4j/jdbc/package.html | 23 - .../log4j/jmx/AbstractDynamicMBean.java | 187 -- java/src/org/apache/log4j/jmx/Agent.java | 131 -- .../log4j/jmx/AppenderDynamicMBean.java | 346 ---- .../log4j/jmx/HierarchyDynamicMBean.java | 302 --- .../apache/log4j/jmx/LayoutDynamicMBean.java | 274 --- .../apache/log4j/jmx/LoggerDynamicMBean.java | 282 --- .../src/org/apache/log4j/jmx/MethodUnion.java | 32 - java/src/org/apache/log4j/jmx/package.html | 24 - .../apache/log4j/lf5/AppenderFinalizer.java | 78 - .../log4j/lf5/DefaultLF5Configurator.java | 113 -- .../src/org/apache/log4j/lf5/LF5Appender.java | 266 --- .../org/apache/log4j/lf5/Log4JLogRecord.java | 116 -- java/src/org/apache/log4j/lf5/LogLevel.java | 278 --- .../log4j/lf5/LogLevelFormatException.java | 73 - java/src/org/apache/log4j/lf5/LogRecord.java | 395 ---- .../org/apache/log4j/lf5/LogRecordFilter.java | 46 - .../log4j/lf5/PassingLogRecordFilter.java | 74 - .../org/apache/log4j/lf5/StartLogFactor5.java | 81 - .../log4j/lf5/util/AdapterLogRecord.java | 114 -- .../log4j/lf5/util/DateFormatManager.java | 243 --- .../apache/log4j/lf5/util/LogFileParser.java | 301 --- .../log4j/lf5/util/LogMonitorAdapter.java | 289 --- .../org/apache/log4j/lf5/util/Resource.java | 156 -- .../apache/log4j/lf5/util/ResourceUtils.java | 133 -- .../apache/log4j/lf5/util/StreamUtils.java | 124 -- .../lf5/viewer/FilteredLogTableModel.java | 263 --- .../log4j/lf5/viewer/LF5SwingUtils.java | 153 -- .../log4j/lf5/viewer/LogBrokerMonitor.java | 1612 ---------------- .../log4j/lf5/viewer/LogFactor5Dialog.java | 151 -- .../lf5/viewer/LogFactor5ErrorDialog.java | 92 - .../lf5/viewer/LogFactor5InputDialog.java | 145 -- .../lf5/viewer/LogFactor5LoadingDialog.java | 82 - .../org/apache/log4j/lf5/viewer/LogTable.java | 277 --- .../log4j/lf5/viewer/LogTableColumn.java | 166 -- .../viewer/LogTableColumnFormatException.java | 74 - .../log4j/lf5/viewer/LogTableModel.java | 77 - .../log4j/lf5/viewer/LogTableRowRenderer.java | 109 -- .../viewer/TrackingAdjustmentListener.java | 86 - .../CategoryAbstractCellEditor.java | 172 -- .../categoryexplorer/CategoryElement.java | 84 - .../CategoryExplorerLogRecordFilter.java | 98 - .../CategoryExplorerModel.java | 347 ---- .../CategoryExplorerTree.java | 157 -- .../CategoryImmediateEditor.java | 148 -- .../viewer/categoryexplorer/CategoryNode.java | 197 -- .../categoryexplorer/CategoryNodeEditor.java | 291 --- .../CategoryNodeEditorRenderer.java | 84 - .../CategoryNodeRenderer.java | 151 -- .../viewer/categoryexplorer/CategoryPath.java | 157 -- .../categoryexplorer/TreeModelAdapter.java | 75 - .../configure/ConfigurationManager.java | 466 ----- .../lf5/viewer/configure/MRUFileManager.java | 293 --- .../src/org/apache/log4j/net/JMSAppender.java | 444 ----- java/src/org/apache/log4j/net/JMSSink.java | 153 -- .../org/apache/log4j/net/SMTPAppender.java | 787 -------- .../apache/log4j/net/SimpleSocketServer.java | 97 - .../org/apache/log4j/net/SocketAppender.java | 474 ----- .../apache/log4j/net/SocketHubAppender.java | 507 ----- java/src/org/apache/log4j/net/SocketNode.java | 124 -- .../org/apache/log4j/net/SocketServer.java | 212 --- .../org/apache/log4j/net/SyslogAppender.java | 536 ------ .../org/apache/log4j/net/TelnetAppender.java | 236 --- .../org/apache/log4j/net/ZeroConfSupport.java | 206 -- java/src/org/apache/log4j/net/package.html | 30 - .../apache/log4j/nt/NTEventLogAppender.java | 182 -- java/src/org/apache/log4j/nt/package.html | 32 - .../org/apache/log4j/or/DefaultRenderer.java | 42 - .../org/apache/log4j/or/ObjectRenderer.java | 32 - java/src/org/apache/log4j/or/RendererMap.java | 198 -- .../apache/log4j/or/ThreadGroupRenderer.java | 81 - .../apache/log4j/or/jms/MessageRenderer.java | 100 - java/src/org/apache/log4j/or/jms/package.html | 24 - java/src/org/apache/log4j/or/package.html | 30 - .../log4j/or/sax/AttributesRenderer.java | 66 - java/src/org/apache/log4j/or/sax/package.html | 24 - java/src/org/apache/log4j/package.html | 28 - .../log4j/pattern/BridgePatternConverter.java | 132 -- .../log4j/pattern/BridgePatternParser.java | 48 - .../log4j/pattern/CachedDateFormat.java | 372 ---- .../pattern/ClassNamePatternConverter.java | 66 - .../log4j/pattern/DatePatternConverter.java | 205 -- .../pattern/FileDatePatternConverter.java | 50 - .../pattern/FileLocationPatternConverter.java | 64 - .../apache/log4j/pattern/FormattingInfo.java | 134 -- .../pattern/FullLocationPatternConverter.java | 64 - .../pattern/IntegerPatternConverter.java | 64 - .../log4j/pattern/LevelPatternConverter.java | 98 - .../pattern/LineLocationPatternConverter.java | 64 - .../LineSeparatorPatternConverter.java | 73 - .../pattern/LiteralPatternConverter.java | 57 - .../org/apache/log4j/pattern/LogEvent.java | 600 ------ .../log4j/pattern/LoggerPatternConverter.java | 66 - .../pattern/LoggingEventPatternConverter.java | 70 - .../pattern/MessagePatternConverter.java | 58 - .../MethodLocationPatternConverter.java | 64 - .../log4j/pattern/NDCPatternConverter.java | 58 - .../apache/log4j/pattern/NameAbbreviator.java | 350 ---- .../log4j/pattern/NamePatternConverter.java | 61 - .../log4j/pattern/PatternConverter.java | 87 - .../apache/log4j/pattern/PatternParser.java | 683 ------- .../pattern/PropertiesPatternConverter.java | 104 - .../pattern/RelativeTimePatternConverter.java | 106 -- .../SequenceNumberPatternConverter.java | 59 - .../log4j/pattern/ThreadPatternConverter.java | 58 - .../ThrowableInformationPatternConverter.java | 108 -- .../src/org/apache/log4j/pattern/package.html | 28 - .../apache/log4j/spi/AppenderAttachable.java | 75 - .../org/apache/log4j/spi/Configurator.java | 54 - .../log4j/spi/DefaultRepositorySelector.java | 37 - java/src/org/apache/log4j/spi/ErrorCode.java | 36 - .../org/apache/log4j/spi/ErrorHandler.java | 92 - java/src/org/apache/log4j/spi/Filter.java | 124 -- .../log4j/spi/HierarchyEventListener.java | 45 - .../org/apache/log4j/spi/LocationInfo.java | 391 ---- .../org/apache/log4j/spi/LoggerFactory.java | 38 - .../apache/log4j/spi/LoggerRepository.java | 110 -- .../org/apache/log4j/spi/LoggingEvent.java | 639 ------- java/src/org/apache/log4j/spi/NOPLogger.java | 212 --- .../apache/log4j/spi/NOPLoggerRepository.java | 131 -- java/src/org/apache/log4j/spi/NullWriter.java | 39 - .../org/apache/log4j/spi/OptionHandler.java | 62 - .../org/apache/log4j/spi/RendererSupport.java | 33 - .../apache/log4j/spi/RepositorySelector.java | 49 - .../org/apache/log4j/spi/RootCategory.java | 76 - java/src/org/apache/log4j/spi/RootLogger.java | 71 - .../log4j/spi/ThrowableInformation.java | 96 - .../apache/log4j/spi/ThrowableRenderer.java | 33 - .../log4j/spi/ThrowableRendererSupport.java | 37 - .../log4j/spi/TriggeringEventEvaluator.java | 40 - .../org/apache/log4j/spi/VectorWriter.java | 94 - java/src/org/apache/log4j/spi/package.html | 29 - .../org/apache/log4j/varia/DenyAllFilter.java | 72 - .../varia/ExternallyRolledFileAppender.java | 189 -- .../log4j/varia/FallbackErrorHandler.java | 138 -- .../apache/log4j/varia/LevelMatchFilter.java | 104 - .../apache/log4j/varia/LevelRangeFilter.java | 144 -- .../org/apache/log4j/varia/NullAppender.java | 79 - .../varia/ReloadingPropertyConfigurator.java | 38 - java/src/org/apache/log4j/varia/Roller.java | 111 -- .../apache/log4j/varia/StringMatchFilter.java | 121 -- java/src/org/apache/log4j/varia/package.html | 30 - .../org/apache/log4j/xml/DOMConfigurator.java | 1123 ----------- .../apache/log4j/xml/Log4jEntityResolver.java | 51 - .../org/apache/log4j/xml/SAXErrorHandler.java | 46 - .../log4j/xml/UnrecognizedElementHandler.java | 42 - java/src/org/apache/log4j/xml/XMLLayout.java | 216 --- java/src/org/apache/log4j/xml/package.html | 33 - 224 files changed, 43527 deletions(-) create mode 100644 java/lib/log4j-1.2.16.jar delete mode 100644 java/src/org/apache/log4j/Appender.java delete mode 100644 java/src/org/apache/log4j/AppenderSkeleton.java delete mode 100644 java/src/org/apache/log4j/AsyncAppender.java delete mode 100644 java/src/org/apache/log4j/BasicConfigurator.java delete mode 100644 java/src/org/apache/log4j/Category.java delete mode 100644 java/src/org/apache/log4j/CategoryKey.java delete mode 100644 java/src/org/apache/log4j/ConsoleAppender.java delete mode 100644 java/src/org/apache/log4j/DailyRollingFileAppender.java delete mode 100644 java/src/org/apache/log4j/DefaultCategoryFactory.java delete mode 100644 java/src/org/apache/log4j/DefaultThrowableRenderer.java delete mode 100644 java/src/org/apache/log4j/Dispatcher.java delete mode 100644 java/src/org/apache/log4j/EnhancedPatternLayout.java delete mode 100644 java/src/org/apache/log4j/EnhancedThrowableRenderer.java delete mode 100644 java/src/org/apache/log4j/FileAppender.java delete mode 100644 java/src/org/apache/log4j/HTMLLayout.java delete mode 100644 java/src/org/apache/log4j/Hierarchy.java delete mode 100644 java/src/org/apache/log4j/Layout.java delete mode 100644 java/src/org/apache/log4j/Level.java delete mode 100644 java/src/org/apache/log4j/LogMF.java delete mode 100644 java/src/org/apache/log4j/LogManager.java delete mode 100644 java/src/org/apache/log4j/LogSF.java delete mode 100644 java/src/org/apache/log4j/LogXF.java delete mode 100644 java/src/org/apache/log4j/Logger.java delete mode 100644 java/src/org/apache/log4j/MDC.java delete mode 100644 java/src/org/apache/log4j/NDC.java delete mode 100644 java/src/org/apache/log4j/PatternLayout.java delete mode 100644 java/src/org/apache/log4j/Priority.java delete mode 100644 java/src/org/apache/log4j/PropertyConfigurator.java delete mode 100644 java/src/org/apache/log4j/ProvisionNode.java delete mode 100644 java/src/org/apache/log4j/RollingFileAppender.java delete mode 100644 java/src/org/apache/log4j/SimpleLayout.java delete mode 100644 java/src/org/apache/log4j/TTCCLayout.java delete mode 100644 java/src/org/apache/log4j/WriterAppender.java delete mode 100644 java/src/org/apache/log4j/chainsaw/ControlPanel.java delete mode 100644 java/src/org/apache/log4j/chainsaw/DetailPanel.java delete mode 100644 java/src/org/apache/log4j/chainsaw/EventDetails.java delete mode 100644 java/src/org/apache/log4j/chainsaw/ExitAction.java delete mode 100644 java/src/org/apache/log4j/chainsaw/LoadXMLAction.java delete mode 100644 java/src/org/apache/log4j/chainsaw/LoggingReceiver.java delete mode 100644 java/src/org/apache/log4j/chainsaw/Main.java delete mode 100644 java/src/org/apache/log4j/chainsaw/MyTableModel.java delete mode 100644 java/src/org/apache/log4j/chainsaw/XMLFileHandler.java delete mode 100644 java/src/org/apache/log4j/chainsaw/doc-files/screen_01.png delete mode 100644 java/src/org/apache/log4j/chainsaw/package.html delete mode 100644 java/src/org/apache/log4j/config/PropertyGetter.java delete mode 100644 java/src/org/apache/log4j/config/PropertyPrinter.java delete mode 100644 java/src/org/apache/log4j/config/PropertySetter.java delete mode 100644 java/src/org/apache/log4j/config/PropertySetterException.java delete mode 100644 java/src/org/apache/log4j/config/package.html delete mode 100644 java/src/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java delete mode 100644 java/src/org/apache/log4j/helpers/AppenderAttachableImpl.java delete mode 100644 java/src/org/apache/log4j/helpers/BoundedFIFO.java delete mode 100644 java/src/org/apache/log4j/helpers/CountingQuietWriter.java delete mode 100644 java/src/org/apache/log4j/helpers/CyclicBuffer.java delete mode 100644 java/src/org/apache/log4j/helpers/DateLayout.java delete mode 100644 java/src/org/apache/log4j/helpers/DateTimeDateFormat.java delete mode 100644 java/src/org/apache/log4j/helpers/FileWatchdog.java delete mode 100644 java/src/org/apache/log4j/helpers/FormattingInfo.java delete mode 100644 java/src/org/apache/log4j/helpers/ISO8601DateFormat.java delete mode 100644 java/src/org/apache/log4j/helpers/Loader.java delete mode 100644 java/src/org/apache/log4j/helpers/LogLog.java delete mode 100644 java/src/org/apache/log4j/helpers/MDCKeySetExtractor.java delete mode 100644 java/src/org/apache/log4j/helpers/NullEnumeration.java delete mode 100644 java/src/org/apache/log4j/helpers/OnlyOnceErrorHandler.java delete mode 100644 java/src/org/apache/log4j/helpers/OptionConverter.java delete mode 100644 java/src/org/apache/log4j/helpers/PatternConverter.java delete mode 100644 java/src/org/apache/log4j/helpers/PatternParser.java delete mode 100644 java/src/org/apache/log4j/helpers/QuietWriter.java delete mode 100644 java/src/org/apache/log4j/helpers/RelativeTimeDateFormat.java delete mode 100644 java/src/org/apache/log4j/helpers/SyslogQuietWriter.java delete mode 100644 java/src/org/apache/log4j/helpers/SyslogWriter.java delete mode 100644 java/src/org/apache/log4j/helpers/ThreadLocalMap.java delete mode 100644 java/src/org/apache/log4j/helpers/Transform.java delete mode 100644 java/src/org/apache/log4j/helpers/package.html delete mode 100644 java/src/org/apache/log4j/jdbc/JDBCAppender.java delete mode 100644 java/src/org/apache/log4j/jdbc/package.html delete mode 100644 java/src/org/apache/log4j/jmx/AbstractDynamicMBean.java delete mode 100644 java/src/org/apache/log4j/jmx/Agent.java delete mode 100644 java/src/org/apache/log4j/jmx/AppenderDynamicMBean.java delete mode 100644 java/src/org/apache/log4j/jmx/HierarchyDynamicMBean.java delete mode 100644 java/src/org/apache/log4j/jmx/LayoutDynamicMBean.java delete mode 100644 java/src/org/apache/log4j/jmx/LoggerDynamicMBean.java delete mode 100644 java/src/org/apache/log4j/jmx/MethodUnion.java delete mode 100644 java/src/org/apache/log4j/jmx/package.html delete mode 100644 java/src/org/apache/log4j/lf5/AppenderFinalizer.java delete mode 100644 java/src/org/apache/log4j/lf5/DefaultLF5Configurator.java delete mode 100644 java/src/org/apache/log4j/lf5/LF5Appender.java delete mode 100644 java/src/org/apache/log4j/lf5/Log4JLogRecord.java delete mode 100644 java/src/org/apache/log4j/lf5/LogLevel.java delete mode 100644 java/src/org/apache/log4j/lf5/LogLevelFormatException.java delete mode 100644 java/src/org/apache/log4j/lf5/LogRecord.java delete mode 100644 java/src/org/apache/log4j/lf5/LogRecordFilter.java delete mode 100644 java/src/org/apache/log4j/lf5/PassingLogRecordFilter.java delete mode 100644 java/src/org/apache/log4j/lf5/StartLogFactor5.java delete mode 100644 java/src/org/apache/log4j/lf5/util/AdapterLogRecord.java delete mode 100644 java/src/org/apache/log4j/lf5/util/DateFormatManager.java delete mode 100644 java/src/org/apache/log4j/lf5/util/LogFileParser.java delete mode 100644 java/src/org/apache/log4j/lf5/util/LogMonitorAdapter.java delete mode 100644 java/src/org/apache/log4j/lf5/util/Resource.java delete mode 100644 java/src/org/apache/log4j/lf5/util/ResourceUtils.java delete mode 100644 java/src/org/apache/log4j/lf5/util/StreamUtils.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LF5SwingUtils.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LogTable.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LogTableColumn.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LogTableModel.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java delete mode 100644 java/src/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java delete mode 100644 java/src/org/apache/log4j/net/JMSAppender.java delete mode 100644 java/src/org/apache/log4j/net/JMSSink.java delete mode 100644 java/src/org/apache/log4j/net/SMTPAppender.java delete mode 100644 java/src/org/apache/log4j/net/SimpleSocketServer.java delete mode 100644 java/src/org/apache/log4j/net/SocketAppender.java delete mode 100644 java/src/org/apache/log4j/net/SocketHubAppender.java delete mode 100644 java/src/org/apache/log4j/net/SocketNode.java delete mode 100644 java/src/org/apache/log4j/net/SocketServer.java delete mode 100644 java/src/org/apache/log4j/net/SyslogAppender.java delete mode 100644 java/src/org/apache/log4j/net/TelnetAppender.java delete mode 100644 java/src/org/apache/log4j/net/ZeroConfSupport.java delete mode 100644 java/src/org/apache/log4j/net/package.html delete mode 100644 java/src/org/apache/log4j/nt/NTEventLogAppender.java delete mode 100644 java/src/org/apache/log4j/nt/package.html delete mode 100644 java/src/org/apache/log4j/or/DefaultRenderer.java delete mode 100644 java/src/org/apache/log4j/or/ObjectRenderer.java delete mode 100644 java/src/org/apache/log4j/or/RendererMap.java delete mode 100644 java/src/org/apache/log4j/or/ThreadGroupRenderer.java delete mode 100644 java/src/org/apache/log4j/or/jms/MessageRenderer.java delete mode 100644 java/src/org/apache/log4j/or/jms/package.html delete mode 100644 java/src/org/apache/log4j/or/package.html delete mode 100644 java/src/org/apache/log4j/or/sax/AttributesRenderer.java delete mode 100644 java/src/org/apache/log4j/or/sax/package.html delete mode 100644 java/src/org/apache/log4j/package.html delete mode 100644 java/src/org/apache/log4j/pattern/BridgePatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/BridgePatternParser.java delete mode 100644 java/src/org/apache/log4j/pattern/CachedDateFormat.java delete mode 100644 java/src/org/apache/log4j/pattern/ClassNamePatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/DatePatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/FileDatePatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/FileLocationPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/FormattingInfo.java delete mode 100644 java/src/org/apache/log4j/pattern/FullLocationPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/IntegerPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/LevelPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/LineLocationPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/LineSeparatorPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/LiteralPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/LogEvent.java delete mode 100644 java/src/org/apache/log4j/pattern/LoggerPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/LoggingEventPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/MessagePatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/MethodLocationPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/NDCPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/NameAbbreviator.java delete mode 100644 java/src/org/apache/log4j/pattern/NamePatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/PatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/PatternParser.java delete mode 100644 java/src/org/apache/log4j/pattern/PropertiesPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/RelativeTimePatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/SequenceNumberPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/ThreadPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java delete mode 100644 java/src/org/apache/log4j/pattern/package.html delete mode 100644 java/src/org/apache/log4j/spi/AppenderAttachable.java delete mode 100644 java/src/org/apache/log4j/spi/Configurator.java delete mode 100644 java/src/org/apache/log4j/spi/DefaultRepositorySelector.java delete mode 100644 java/src/org/apache/log4j/spi/ErrorCode.java delete mode 100644 java/src/org/apache/log4j/spi/ErrorHandler.java delete mode 100644 java/src/org/apache/log4j/spi/Filter.java delete mode 100644 java/src/org/apache/log4j/spi/HierarchyEventListener.java delete mode 100644 java/src/org/apache/log4j/spi/LocationInfo.java delete mode 100644 java/src/org/apache/log4j/spi/LoggerFactory.java delete mode 100644 java/src/org/apache/log4j/spi/LoggerRepository.java delete mode 100644 java/src/org/apache/log4j/spi/LoggingEvent.java delete mode 100644 java/src/org/apache/log4j/spi/NOPLogger.java delete mode 100644 java/src/org/apache/log4j/spi/NOPLoggerRepository.java delete mode 100644 java/src/org/apache/log4j/spi/NullWriter.java delete mode 100644 java/src/org/apache/log4j/spi/OptionHandler.java delete mode 100644 java/src/org/apache/log4j/spi/RendererSupport.java delete mode 100644 java/src/org/apache/log4j/spi/RepositorySelector.java delete mode 100644 java/src/org/apache/log4j/spi/RootCategory.java delete mode 100644 java/src/org/apache/log4j/spi/RootLogger.java delete mode 100644 java/src/org/apache/log4j/spi/ThrowableInformation.java delete mode 100644 java/src/org/apache/log4j/spi/ThrowableRenderer.java delete mode 100644 java/src/org/apache/log4j/spi/ThrowableRendererSupport.java delete mode 100644 java/src/org/apache/log4j/spi/TriggeringEventEvaluator.java delete mode 100644 java/src/org/apache/log4j/spi/VectorWriter.java delete mode 100644 java/src/org/apache/log4j/spi/package.html delete mode 100644 java/src/org/apache/log4j/varia/DenyAllFilter.java delete mode 100644 java/src/org/apache/log4j/varia/ExternallyRolledFileAppender.java delete mode 100644 java/src/org/apache/log4j/varia/FallbackErrorHandler.java delete mode 100644 java/src/org/apache/log4j/varia/LevelMatchFilter.java delete mode 100644 java/src/org/apache/log4j/varia/LevelRangeFilter.java delete mode 100644 java/src/org/apache/log4j/varia/NullAppender.java delete mode 100644 java/src/org/apache/log4j/varia/ReloadingPropertyConfigurator.java delete mode 100644 java/src/org/apache/log4j/varia/Roller.java delete mode 100644 java/src/org/apache/log4j/varia/StringMatchFilter.java delete mode 100644 java/src/org/apache/log4j/varia/package.html delete mode 100644 java/src/org/apache/log4j/xml/DOMConfigurator.java delete mode 100644 java/src/org/apache/log4j/xml/Log4jEntityResolver.java delete mode 100644 java/src/org/apache/log4j/xml/SAXErrorHandler.java delete mode 100644 java/src/org/apache/log4j/xml/UnrecognizedElementHandler.java delete mode 100644 java/src/org/apache/log4j/xml/XMLLayout.java delete mode 100644 java/src/org/apache/log4j/xml/package.html diff --git a/java/lib/log4j-1.2.16.jar b/java/lib/log4j-1.2.16.jar new file mode 100644 index 0000000000000000000000000000000000000000..3f9d847618bcec9c81f36ad6ee76a0f53816987f GIT binary patch literal 481534 zcmb5VV{~Z2vNampwrv|bwr$(CZQHhO+fH_DJK1sa_QiYW+%vv=zjxOf{jaM=_oy|i zdd^iXF9i&O0s!&%h3@p_0r;l}1pov!4gqZy_TBYAu0+#<(lvHCHj!W22ZwWap!l&#Ob}e zE;G$ppgFTQsiYDm*#Ktbd(X$0CKa>y;61l)q6EPT(mreKE`*`un@I==;~KWi(-B$q z1z1S_F0`XpPiYY9&H*n{x>5tqa=2Y^%|O_+<%kQh*_?*y03+{rL^*?SfY!NL*CPgL zB6_{>nLR;(60gh1%Z}HPJs{zpaJm2u=OT81f0N`Zci_*KUqU93Awp_#zk@8v?H>m8yHkWx!j8DxwA}NbK75Yz@xtW+csQn~Y#l;^w zPuR57Nz)=17>=3*8}V&m66@%uyVQjrcY1prBfG#;gLU9AW~&YV@xk>8sXb-pmXVLq z`C`Gn_PQ=~k(*Fj1+G)`rsMJT_r$AZE1cloL+kHm6&IQHUtyyzu-R82X_N#(uT(is zkF_a$kzNU-JfLvJmM@xjoHie#Vg1$(h3i=W{y?%}%*?lgzPGMI8JIi1(t++pFv)Mw zJWC|)s1po=h+IFO7B<3z<9CQo82r0RvJ5V94rLH*BocrbrlD=ZAU9ci5jLsHDB)^E zNEJ{^W*rRN)#7(bfq2+dA&(T8-;z7mpIyHecAKJ#ZlfkRL@RJUvkh%bnF^pKk>;c6 zizqtZU0r-#9bWBIXI6n2=#=@xV^MXol)b&Vy~GVzOBfNcZhlOsJ(;ksUAuUS5w;PX zEO|5ewzm2CcHzemc$(P5l#D-5Uz%TWasdw-X$Y?}iVm6c_+PbwHbD8Le!z8g!6j8p zrLHs)X*bAv=3=C8Lo^uan+z;AVQ!7jnVn3G#_Dp;!hAPPZPJ2Fdybc1!dz8*p>ebT z%Ph}Qv)Ph&R$#8$t2XmHlpz(!HNvf#`>Z#uT3Fm?$1Mo#VK&;pNDe6%Lz}Lfeqa37 z3dtH#>F}ZV!*7*R4oY6rDt{)|Lz$AY397bGvBwUW!Jiwp*%sw$ky~@d9t(F&mjZ`K-2n4p2L=Fe zgaH7M|8Kzu`4@=)ANYX(P6UVeH)sO=CqDmSxX|%mQH$`;hSCy3BC<*%+nQQQo2{t6 zFLl3Im(Fis)La*fYjVg(x@?!ZH0LaJ*q6?Mg#(c+6ZHWcTQ2HuJN85+x?Icbp52k8 zh~VI6kGXd__n_}f_@i`Fxk+~}6LN~fY5h#x4yd3qE=emy}CMecj)cZtnP3A$a-ti?AzkwZT zG26Nwv@o!G&);QJj;Xo~HRz7!QmAHr>p#1)Lp0qcqvj*gEDs5cJFH_?{m6WHaGt8d zaC1VeJ6n;`T(v&Mw+WT{|DF`xia$dp9fgAz|4mIOm59^~z}(-Y8yx`N9jM8>N-wU^iA9Xi{RKwd5i3Ezqzw6nlIZ>U2M=zF35BV@F_^s8Tc(6lbbC z86A5t9MXGKVp?Ync+<2;sx0XCc`FDbnv9ekJLf_9iLB8dsp{%jHSf`0hdq^}R*mqH zQ+OEkVxcF|=rnc3DvB)LF`p0FF?Q`7muRUI#Oh@th2nGnRkf?T=n6JH6bc_WIsmKh z@&wpEL4{5dI39D+4^aZ9CR=RgNtX6wIGWM!-||Y&-8J0<=eIxx--)G=it6F*4tRms zyaI;`*rRog1IjakNe?46Rp);x@gZLp@E81 z-PMRWff65F$Q)$uT;>7Y@Ybq&f z1W{GOLOHo)flG2l(%LRj@&}gR>fsnCf)d*%J=Q$Ac6o^cJPO-Ar|Jf2?D)H}<3W+`B4VlrGux1})w>uP4sGz^$JF@CEPk)3;7 z_q2E=Ij05m2@*GEvEa{fgx@}00W4e211jPkJcfkvbMuwE00)9wd++bScj<@H$v4_R zXOZ%kCx#kDKSuON02K_SMM_{jCa6cM2Xi2fy$*fipwy05R@q%dLHmr7%tU?%b9)|? zSRHOfcz5M7za0?)qCk(4$vFf^P61*qfv9i1t8$K#Cpg3GtM}qk)2YG@sMunLe`N?V zUoexhU{Q{FPe4~kRsok%c+w9?ItSFN+?hc1n(M@e`WTML-)(DeRd_S8l+|c?h>yEQ z3;G9f*=IrG@eVzVlZs;-G$H2<8tk7O@N&C!lQBELopwvB)Ps zc*m>1lqqjQe@K+$5-he+&{K~4fVHm*B_=XIUS@XU3g91ivLYMu`7}P~jmG%A_)Oibl9>N}_D5?oGr-VLB1H^*qWl+n9^NZCo#U8OVu0l@n4(%MorvAO8bar5tYcu z%pB}TsFLK(jwlm(w7am)NI_+9smfH^8*g(+kx|7suwYIN%N~~_r*j;6S-O780K8O2 zfOdAgt_z^L9I-q@V5eYh>LsS@#1FrX?x%~G4pm!6KOdb@G5T!XyT=bH+a<~6{ba39 zFVQG6Lt~%~EVn?l#dZ6dfN7)H*_HQN!?2sAw`y75)0TgEwJACFDmmO4+6Dat%x6GA2&+%OKbu5w zgp=-Q!X{+t?vpoqN2+Bsj&f=mH@CkixdNVj`VyO;7-Qha31l(}|^PR~bsbAwj(aXh2BwR61o&~`g)_#E|m>F5IM zEcOU{M=UF*!xcfMUD3#BF-ee_H0iGdZtggtyo|7+6b@>%Lw~!wi=zL$vf}uBW83lm zI5Wpy^&$A_?ey}8eBSZ?@Zsy>8y%{^ZR%rXCQ}bz-5nDj&26tf?iStcU}&rhzU>mb zw$)o}eQyTnYc5L1pyy%?7TYrJHy+)CR5kDfq+N7#&8MC)Gt}diFJ$SqB{$I!SU4lGUlI51G>qC~#K1_C z+kpTzkE;=fe%q+B*Y;7<(42g-%&t^65)nCu!oB8&X|r~Wu~M845xX+TTsLH38vsUk z2d}%)F@Ze;TtYrR{6Tj#hjiLXP^r}7@4j9@%)J-{UE8|@PGH@+k;36je^rR_C~kVi zg4x7_CAf+Y7M(bH=)4|VXqx6N%poQDOlMiTO^Xw#?i$r#asZd&f-A|9urm*si-p@? z4rRaL^k^l38QH9mozYE~UMW%TQ_~>0$GzVwL1%;B4nC-H5m?wT_%nE21-B$cgA#a| z2hP2pM5We@+Xxe!KCBS24&Lh!#gU^EdxxDDX(a`pSSTk4`{#li%obS>naOxgxi?B< zTPu`duc7JyOto_`bEGzrkU$-AYtk1zn%TBf-zFIKnVgT;s~-?rIC8M<;Bu{tfpO=7 zCQ1{gZ=1}g&5f2rfL~2rVqBiLtn8mY4u-(pjo)6_&jd5k01ELwFQMP6yk4;@8spp~ z?oPj{(NVrxBhOg~=PiLXj0#}!2bGJ&bG&O*lsExFff92B{DRt&p@^LR#QDt<-7Odo zs&xrLkO`{nKAYq`X=`g|-MHQR1ki{D-R){aU948fxcs6gK=MBj6ro^!t_Iixp|Qr%mA_x|mBo_J&OL4qpb#3Jv_W5(=0iEOZ|PJxPeJV@gw5p8 z<^Vo6w~xW#8Pa6zd3&Po%g;n`=h_+bnhNn+0y38pxP(Gp$jfD0l==Jh=1CE67fdm3dW8@cwch+_5 z#U%}oPC%`Q72odLm|vEb6?G%bKSmh*ma5C`1&8a?+|>@4Y(BC|e^6|7J03ETiIcG*e{f+r750l$5Z?dxlq=-$Q6yY@Na z_wStj&HnMy6&?VaGpLInT)E;+`r&NC!|c^gR2 zfIpzZ};K`-~8VhHBk^5gKs6a!?&4&C>%0Nps8}{eP04{n5{%x73g-P z3JR7H6@Y3F+mOWvjxX!^U*%({%#5D(h2MR92qLeR(-=aVITn=zIL~1JCNjK>fX_Jp zT~7Yr@eh&VU!}qSvS1qnR})+M|4F0xS98>V-tT{yTicnjSpKh^VE=bc{`amZ|7TbB zb~beOj&}AYj?NY)PQ2qiP=gEz0DHW04)b5*an2OZiU{Csz5Oj6qvGO&8R8#*Y<&;e zw>X>Rjo>dKMSv7bu1DIPBEf5UKZaSK5t^`4XYWV=%LY0pqLW@;n-M$eW;Z&6y02LV!|ULufGe8Tdi*UJqs9!QU5 zD%CdA-4Pj5{j*-32CWPFSI-d=P}=%ZOl@u8?192_qbB%_P_)8&=*YkxCkt%bqo( zU8(LO!u2OI!6S`e>T)0>=*o3GjKf$l(EB^*5yhA*8#xnznGY8ziZEy2yFx?333hu< z5y75khjdZewTy`pniDm9z}*HY9wkQN#r+o@n9jqEyFKIjb$lNEjc@mxy_hdwZx3&8 zZx;u4e3q7@qmz%PgPVgB?C$Ku{WJYh9r{{XS8zabKKh^J;&I;kafLt{{|cJs(lfjk zpuVZ#H}7E|3}v3#^Yny<^hOrZ5^;4bhlnPMfR21%PlS8vidgH|@oQ(CvfcB2$yWkI zNV2qvA=y*ec!Sv?f9>5I8G$JJkyj2044`%aj6fiLJ2jr~{ZpO9U=%W*z{CyyPy%%X zX~Cul!&fsraKlnDw5e!8Es^yANS|T60CfsX0Lf6#Sm=|S3$M|DOkPI(VVTk1IO4!T z4gd{rK@}l!}6BDyYcHShXgX~ev{9UWeB5_N+V6d1{O9PR0qN9ckR>N;D-m`~* zH%yPnL3KQrnaT)?JG;897k0IswVT|owUy*Bxx}>P_EA$ilNtzx_Yi|+h)Rgd($FxU zm;UXa(o-4QTp+TqWO9lWNhhFT-nm+l3m(`HQHFjpSKS8t_4%Ms-8msiYrrTjGc}e_ z(x5I$y^QhP?5SvxFSoZ>{(4pOIcCt#)e8~UG*@z=rO}HatMlU1N0XlKc(I8Gys`z1 zi)+05uCybe`iYTpTvlbXpL`8~s$|}dBpN7Mu*>Nu_h>En@Mm<q#PJub@?YS(H0hQKg4FQyr zR>x6vO1mOd;5VAP~Z(|cMk*28dza521Ntw%+{o3!vY z=Lm{pGh2^&D#t;!G<0o=jxAx|?&EJC!(9$sdbKnKac>~M#+_LEvkbPi-efthG;6Bl zM{3={jEVT1j~nymSH2Q(t=K%A^2eAy&$6n@6P@>%7R#SxIzXeN9_XU0#Tv>EQXb0v zIx8a;)vuBTwgkRpo9!-7B==pKt5aorhUr@0Q_MTNER{tXHp(YJ!TM)tyW`Pjj=GN? z%d4hlz+6Ri8Am@zBcse3gmK?e7uxKm-$(2IDi;?@^s4`j@@dbEDCdPRaPn7ljAIuf zl}%<&Redi!BVeNnWm5#9u6s8Sc{}_?)E;jgO~b?yvD~{xW{_MJ-*1;af?%wmW!*UYSs~S7+d45oa^0?M zY<+2U2xfAf{=gr$y^vXvT$|Z3%5hhv>aMP&BpF5m7#G6%EtHe7t{KLiB^o1(ZpvtB zj#(yKbSj^E%*AaIRAy;C9U3J6tcOxyH;^r3mqXFa89B);3O03IHJ%d%Ji>m+l&h7g zWtxRGBNpeqXL)|d)Iw^WFiK-bqCtIT9(Br{^O=(wnC3|onang5axK;$bdV14K{tst6`bQ6rpPMAe1%HO;lM1qF77!>ApvdoUzPFJ!a$x(BpFqm;jL9251Rl00ULe=g(W2~GNQLnxuIkH%Mt@TGnH z(X(r@->&*-9u^q&{dRqT5yQT%)h?G{%E8nh-~3R4_~u(o?oLdY@}IktsbUCz;SXfP zJC_!qxw1lDJd3RMLoE(n7z`tIGxJ2$hIxdZ5J?KAaH>d!D*n28D3)jg)orne9Q6`qgAQDsFjtNV{18@mivn2S;rSpElGh1@Nyd*^)4eh7GIGvBN(yjAae!g z{h}V3Mo+#d%jHSk;!KYG^hRNFS@u*LRRLG{aBsJ}#GG=@!9W;n`$bJZ;Hg zT6RoMc_3hrc!(lR%U4M8%U9x8*|yy?Dlv-YUXgM0^2X%Jgw zRPX+-u1>AOXTqXKHOKN{3IL-q#?+E3tXZB?d$_d%a*p)K_9nJIoYS$b$~dYHtXk`^=ArPa)KC+I+>$SVCdJPlC4$OLL^J4e4MgsUlB#8W>kfrJl+ zr>=)cjqBv*-igI@xwNcg_|ei+n`>VI!ih-LOZH1;`{kkuuRJ zcmr8%zVo|$Xok9eLScwwG`w01JQo@1o-OQ90B|(#(9IWVhOiBoi~#kY_>-=-K!31f zrwMve8$X}cL<^iWd?4`DGC~~#R+FCBX)&{}Qar(KraHtQP~CWC%Dm_ORQABx8}g&A zIjW32B)DOKwUpnDil!>yIR}pqYyLX)Gx42(rVBdcx$lPZj`nRHliQ?ZuRg}vGtK0;MQ^* z?DIIM1lFe>lRpu%hbigUSEjESe{3O#*2*DL53^}%Dpxi95dMyJFm48a1m{g66Pr?f zO{5NMV(imWT&_8|Mo(MpH#KkG}UC(E%8yt4niUv8oke)tvr2q zJoo&;jC4fl%VLcqco)~^C`fIH(VS$J}OZE;c}o z7L4Y4?>qTGE@6NQXOU`vgB3rRP$M&sMz3}u@4O8>`e)>40)1PRby*dQ|H#4cP&RJ{I;BY$f!ZzOiWcuYskXM61je8x=;c1Af z%y9I+3C29u3gT;)`)MeMAzD}<*lV=n38XIJN}W)V@bK$*vv9ghJ3k>`3}ks}dIDW2 zsU?8Uk;qB!-13mnqZ`o!0|CO91RPh1tHpz45jM}^vDd1PPFdTvz2MY-H zZTHnLE!wWuu0hw7-h-=v%(491omsfrtX66yU1TfjKxT_pMCi9AP4v}gqrR55XcJSY z)nZRAt89cSecpuwqe&LKKID8_Xhg(ndq2T||FM*$? zcOO7@oE{f9*Q{d^aQTh$ppEK%HtybVDv)}4ZCq@TxYs?hcRW3a;K_JAJk*N0Z7|ud zy5=D4ma#U@o7qr=5$2&7eZxrlI6cS)+5#dtd{JAz7-vJzB0`C$AYIyRTo*f|7TcaB zt7qj`d|Uc853*p-jrpU4+v@!RjyJvV|>3H2P61Wsbf#IkKbv>(GgB45CHBJ%mEQ_ z-J>3pa@7!MGdJ8s-X-mM>l9n()3FXKjR|Mj_QBr4o*sczL$0>Bv@_VS<$9c>3}N5w z2}gDde@)SAmQC?_6dc7Qnp#$oF13v-rFw}EHI=`kiqAWfP-XO#VlPlzmt;&lRAHLu zF#4QChE)&;=yJdRfzCVta)*iS`W8v-%l=Ne>at5YWY{l`5vkb=tqEKF}zZ3EMbPS*hDY$?3Ms%BGvZZkwK>g0-A+u1h(rHEzptn09?_ z;^7M?5Sb?4Kafjc+4cR+gI)~RZuWvOzH5|Z)@)$MtYdCkGBY&6l%Xa}GKxTl^JrPo zFf`W_V-pN}!W0%%w}`v_%q*vStu1DSQ>g$(Q+1PV;xOaIn8VhH+ljL)iViQ9matCb z9lLruH(0R?;n0Y(#hxB=T0X<0|N_(l--Enk(h#SSQHvF#V@AMNwPHyPRKRs5bGR;$eZSCjv!A?1R2>`T zTggvpASM20WiIlOGwTQVZ`#QVdC^HTENi zNMLAQ=nojwn@+V{eCQebWqe9n8$8ETh4tRVQ)F%^UT&X0jI9LiNRA}@Kj1$Xn3l2h z?XvvDq2Bxr6kNTLAVM1|X$BNPY5T?^cj6M~ppG2K0@eq#k$wAeEIlIo$b5&bbHEmH zMlxKI)}lmsmNieT@Y?)uQz|?BDx+PLKe2dqns;k7DS`#W--V@&LUt0`?|)YG$gb&{ z@~kXYD70F(^|n~jO#+pf5wQ*IBj%H|?>xL%Yu8frjw5 zIi30c{X^1km@$_I1OO=h+k^EF5$<0DL;gPnM#;*=+Qiw;_CEp?qpD?(tb*|^n`N5h ztVPgVGQS#1v`%Id6iI-d5O_=!KmmE+)D4~G(r zxf_hBmwm6YyMmeHFuEBO`5uoWALs55x!#Z0qm*(XG=A_rl*phw z1gd;sJG=yA@o@hL1CAgTCZr{V00m%l_*<}hAt(bCnkNKU<4X8m@jBZ`JoJDdpFiXM zl`=#bBVOS(bKn&xBM*HlJ;+@l$6aD@6Vj}NyT%|jjIRtP`vcn0Vz5E!p`@yWsk=7w zqLt_Yz4DHA~OPWz$1y-qL%#uV# zR;j^_@>BAtOem@o{Ar_QciS04fFNdVacfm3DFN+H{GgG;x}vhpdp&;zCa0`!gMg;B zqRYzfY59#Ehj5_Zro|NbppYu3UXMjWTRizN7Ovr13{=!uo}=w)W@>gc+!`BujC9+ONRFDtYK|Fh zT9$v8&16F(-USzRkPXe*#W^Is97Wo{ zco;8*xWxMdX_$%ND~ZNAL8@pMcT}gEZG%EOq&wzP&vC}Omw*+oq5+x$epNg@m~X~ARm*h^Ve!&+Ez&X?aXjdj{Fkc zk#_8KFz9QR?F`{&I#8dPXB{&QwbEF$?3P;_{dgjwpAC+7bsk@q?lKWZOj?LY4^}hj z4uyq*Eitg2frpPGVYi_#&ut}&Jd_r8Zc#o8kCt)Q8U0e1;N+9+w$m>QW;f{$0mK}u z*A&H89aj$lC+Vg=#3jM#^rDH1Go>Y}hY3^kreeW`$3A5w*bj_$b5IzyB{9|>G{L+z z$g2Or5W1>Qh%-)R#3P)8Zg%EHBrQB;r8Rt+t(7K(JUaZq&{zNNgN-oQmzI1F6?Io^ zes5$5Ws{>t<4U96DD_(ID&qFdgmLjax1Ckpk1gi|N>x}dC{ieO@QxSQa^!9wD zBdgV4JNq#EkKu+}Ta_ZHX)?{stNSV;xV< zk#*@?h#oC6a`|n}5EC!58((USOw;8yG**`HUl$(kI+Ti(F5 z$RU$|Qkn@=LGht~nG#Z=bWkK2tSMrEmp4R0^M(t-{g{UB(sK-vhD?`Ft|t=F^-UeO zWwq^jVgc)FvzBPr45LyGk@f`B?w@Pc?|(T=F#jKGmXn9A(f@m*AQ853GBR*9R&uei zF>v(wkNTCOY-6{iisEZ~xLUyo+8A5AJu0n5AhFgC^Jj4Cw~Iv=s9>OYFv>Ns)3w%G zijmqH9KGOY)|bBwP~_^sn2aI*ONd^OYi&-5Yb={Q$?1+$u2b%5w%3i%+xr^&xB;RJ zEY)nxAxPwd(qev5wENke&I;ox%oSYmVD+8ricd3*ZUspC&bDE)nkwvM7h2;pwa zP2KLXz%+vze2A51S8C6U48Jq|P5MHu*{aamGY~tVLVr2#ACk-!jL}-p0CalhY}#HC z>vOgsMOfTf`|2Ce?uAQR$AwllpGK660-Lus+WiTi-SXj0-C>4?6OmTZY}LBWmSJG9 z9yAbbWgqHDj<*?_H_Io2dNnsUk3D-vKMq~Afu%&J)I$cG2GBOc9lN0n!}A)fgiF!d za$m{Dwrvs zjx9z!I$owuo=BQBvCySMQ5-BS-d%R2ufM-CtfHWYpso8(5uQyB_us@|#&*RmZEx(e zBDwBIm7xu990^&ytBN97Q7^gJ?r?|Be`>P!feff9OB3~}!Y;L}*nYd&*6dpESngJ^ zS5;kWHWeItEh}p;IpDfTcngUH8QkZ@#&GE-6DPZQXqUbFgk~6srM#XYlrraL4MK;Y zE0r#ARFj_S(JWw4k7P<$D;!{3G+~laKKE^kOOzpLP2Y~0;&&oO){0b&w!TM6ja-R; z#?Po=v2lo**eu)9VX3kg83JTYqUC@vM#Ce?dxb$@uGWFbYK$Owqv@7p9fUeZHP-sQ zdy#5g14?;m1-~49$>Ieo{qO>-7lO-zG6?c_(oaOz!hD_fnq%enx(v)Hr2yoQxB~@Earf0A9KxJz38K$2bL#egqy}O21~_DJmqjEMZ`K z$FU@Di}MaMck;rpb4gbAU-b+ne)c)b8C+Oqx{t<+kJQrl;`o026-~C((8n;Pi;K{( zh|U-l$0#OuP z^IvB2pL`%iRYn@c48ynA&dlwP8HmVgr#dS2MI%i$zxif}rkb#h&aaSnEv>L^156y# zR_3PgRkqUmvkpi+&$AFIF9t--Oy#W1^isS`mWuO=FuZPdi-Qu$T{|=&YT>7cp52%4 zAD-K*@AuCzue$=&ys!oT7r}6YK8_vqF+c}nB98D{1h@jNa-7&bh)!c1)T?TDoTxnx zN5rRmH5?x%+0X{8FF}J}6fh87@PiyN%uKVP4sfR<4g#ZLOmvg4;e_&vHZ#tcSWc-r z(iNnv{M98eRqK3-lN@R@-lfWm(}AXL@i+P&=HgA5s;8pMjI1iIF2pHIxM{}wfj37g zUGqh$7?$W05dsJCT&JT3>n$vc^`^<Ep#oZ(;r2@)L zBv)BjyQbuE#fUYgpj23B*A<`nx~vy5UlbQ~_Un8VG&By%Ye`wYxHRIJ>uE;nqO0`! zL^&CAGRtEY$|*gULQ3{?5_2atMhb1wxmjpHyF+TAuo^^0$Drs0 zQW(n#;+g}2ejCT9(NdRI=|hNs4$YfHQ?$bfjg|u;rv@VNB?$0Z>2#a|4SpRjD6I5O z$}7#t@A)4ji#!++HBdv13V3Ge?hs7XzAxr zWtoCHu&$(&Cv)s9Lo#g~ce3MG|1_GK*NW^x9l=g5KpA_8tV|44R@NBA>rfkQQmJ6a z>5ccKtBO4`@i=moVwnKGJl~&)xL-HI1ffvvRp`aiGq8eNhPtSXM)m8qB}+xXRms+| z-FS{5W05X-EzfaR%-Szag$*D~Y-V0q;*DaoCZwrt;P~y#D>uSrq*lS}%wT{CU9j@= zz${FPF`H?W@G&>t0dDtBv)fA`8Qw+hF*8WJ%)6@{Pqn`8&Gl7%DDFqzw(!9lYffW) zMKW0X2Xi&F04Qi`$1VQ$Omp_&_`WT(-x2<_X7uCzO5o6G_fO4C&6E0QqQ3gnqLv%*J<1_K+aH z6WJ=s<_#_(2%tsnVwaqj6wGmUXM<~83ZwwQQkbKhe!p(tUeMCHw?sFZjk;JC{|&Pe9x=xR45-USAAs%0m{vBJ)WA;MnM)(0W!mK@)(&F*LPsHZN0puw4r zHlnfkXmooiJU2q37+V57Vl?o@?tT5}>Tu$8ZoK!@tD7lKaURrP9LM*Pv{>}1m+mHm)N zPCvO#Kl^srr5C)-oxAtQ>z?+upXGMg#}@$pP2h7jZjX^avdW;7U4!Jru??K-IN?@z7psYR#z!`U}9=fX{6U;k^*E#8qTp&NkmT%EJv}au` zg`)U_Z3i*m!0MD>WCN=^iX|ZA?;RWya!np{Cry83@n4^9Tmp#L-{AiyA(0x(2C#tw z0CfGml=a`IWRm}DO8(DdlRBh6$}#Hqw{cUFH5nihP^dUWWPFkeNM(Mr1VpAQGELb) z5ZnaGM*D__8T+s1R;v6K%c=uVUDUYp6|`zd;mY!R^OiErmR42I-$GVZtu0}Tw=>pk zT@#Mt;~#N<^YHfVSJElJ?~{XZq3H2zJ%|>o_N@?D>q$N!yLtOU7>%1ll7AMcHFCYq z5G~(PXS){YR_VSgtlKLC-tL_N&Q0oA(&^8~6ffd_L!oBXsRnmqJQM=jAv~DL1~Jg9lw&tm9XxNu)TdGV_2x?CqeU@U;F_&>`IAlVp7k3@u@YNorL}_lp`fC1Lo`nUmZbm(m_V zjH#7w(&owfpvG7`Ps1F6NqIKau;1o(?JnwoPzhGeEwJ>cl2c3B7-VRSjzcG*h!2TE z@=CyKSWwJMTUaq5Uw~CbL7i;X>_d#Ft|pPw#DCkG#-ulIB8I11Lt|=8g`-J#4p4DR zx2>JCDoS`1TB@b_kLs3yw-icYkjnZNNITV>-|z++Bl^Bi0zK5QuMoaqWoN^PslJ043D zy)>>$X*j@gHC}4jKCjoDB<3f%QYx@$fvh;N%^Iv=Imp%6VD~5nIMa+^LLrOFmtil+ zuHxb5jWOX)ABso8H=FEVE55F4x7eEy2tTYUKP8Wh^* z{HXx(O|my9c!%CybBL?)je2YOniWg!&=T8s$P0UW`TEQJoAC4}!teZ5-dfl5EVJNn z*YS*hy>7pc>pLtC#gb1!2O-P(;uQm2I9^sKU{+DmJPa$cqM;cPuzSH|RgQi?RCw9(=$=p-|?yxbL# zZu#CsyV@v;hCM2V-6(1>8gYsx3wIjRG+np^#4pij7VY<hA*%He1xfb4hVYFRbk`vFB^Kt%4F5Y1vw;DbGQJ0j_kMRsRvND1?@BSuLKW(|{*z zG^jRQ85(>k(ZH+p0e(e{Vec3VwtJv)(g4)T}_AlEi#6^>dS8FYt^XnCb$Al>?Dg??)*rXq!+ zA!qi9#oq*~Wg@8=h?GyLW+lv;zLg)xCabG#mGr}270%iBMQ-Pd^{CLTFO3WJ>EL2W z$(?O~*4btlwTYF!Qr(L@NL)PR4yvtMek}r%(34V7lHA9RjY4JIz{X=H#JVeDJrH;I zU-Gb&2bN7iE%GnHWuhi${u~n;ZK5r8Fv-N1VGTSdCEOY9UK{<4Su-_Rr^9IXQAec#f1UiqI#oHu4!pF{T>Z9Uo z6ybDtl{Mp;!X&?=b2S`v>hO%hQLk}uIkqjI>=`%Th?(|v=a@NI-h{7l1fshUb0uwa zqT>C!Lrjy(-ihm;oD_>+i}SXPvD%2br)}KduOkcJPOrxX{h~N3oM-lrV7d`OgqNdA z=kYHx=Jhw61!FJ)Aia4%(}``jf;=UTV}O9acx!RtDxHT}ipYpyQ!a>lWk4eobn(s@?K>f z1CVT6D2^leU`sBYWVUfh{H2{%LzYgrhoN|6EyPwfHt8Aaf2bODy?vM~Fs-c4qH?nx zP-IeOiO|GU?|I>Bop=l|`7wW)K|lLoBlL!+Y(2*4MF2~#wQOR`b^&a0v5ETx% zjN;K+5L<0ZKaPZtZU#G;H&AzQi0&TMGgshDA8S|fI|doeUB|h>4Z(@2p%eZvvWLgb z5Wm|8d^JL|7gY0EzCl#V%m9l@K`3kGZc}r#>kh%S5bKnx#N6iCAnApK#~#-dK{u$# zo>opb%+Y!TXUiYC0hfFqEjw~kK57$_){Q~)#yIgxpK416eoIPjYs|bO53w=nHbAEx zD2`e8Pr@MrUjNe32N2`jo&irBuRCJgKB=KDnKx+NzDnBr302; z)H(TR+0HlZln}U*0;yyDo(Im-n&N^eMaYSa)rpzhUl7HKSNmmjkBDk0^^@=k{$>;{t4q(1!vEoUlJs2Ev z)Yw!+5}P$3E#6*vo13F5!|-^JEKcQrBU4^)1&Mn}bYJLX#3(n#B|khjQHf?L4N2sz)KFJDy9!EGu2#BSo1#oKcu~5aHa3IFZ%D;wr$(CZQIU_ zla6iMwr$%^I=0b4M<)yW?z`7rXWdhEYQ7(5)%@^|XUy^ZG?tBUim?*XP|4G5`E-o zO(C)0Bqo#jcZERXi#rmo@5!$3_o-=goK{g^tln#^&&cKt^~S5mHX}Py0{(1kblJDz zC%Xd#jPdW1#<&Nnb<59D8KvIkOlBR$)RUAdV3(m|JATS_L5Q9s;Z)Lz7HL3ueb&Dw@Nz-a%4E{(ORtT`yp^)#%l0nTj#4pp$aUzDNxrJFFNm`X zs%en9}^ zYx|VK#WeFYZEVP{21=$(nZJG5gf5+|lHSp5-00mQsgc@{S1H2LFGb(PG2HK`U(y|? z00ZTWwdX9aeecQD^-1jwf1D6*EDVjobf0aK>(;!XX`P+GV2I#(Cm5R{c3bEh7Zq0p z%-J>p%)U8Hxzz78!Oc|K{<=f3eD`gB>xffcdw@iOTk$COC=V10KbqB@<(%=Fi|HYE z*JQWyo=eeJ(G|~0ci(2}UTNQrI8VwvobU?v!vu2zlNkjWukP8KP@_kkA2EMgB@yTng7V~xest(iK^?WE*b#38L<9$LXt`=F8 zc;mGfKQGJ_)d9KC(4L2qb+nv_-?7I`hu(<2g^=%i(#hwji5lf?%D06aP(pqjKbtX; zv6eYi&=arr>u*h6X6pL1t1VZh8jX7Q2}@rzLOT|$EFb3u`syUMj0>Vpy>0(`N``aR zDA*=|>Nv)-p;;Wq!Q8{T7inzc`@tdNSw^jAa9sxoUm7QEN_>C+>+Hv){g{j(`q|xI zut2U3$eYLpu+(jWUl45!V0;Ah$w7jB*J`$43(EIfgt^;Wd<9qtptD*HpmVs15AnSifnVcWz%-SW|n2W#fx=&8}ISUOpI6)Z@uL9 zZJKAg>)Ge)#%t%!4|vu0>(W_4ft&^Y6PDlMAk^-CED-;I9FOm_96a+@4X)#s*~3Fy z@0a7j4)5(Qt;cJ-_kEQDT`Qa+10Yl5lO~XTEqxNtxWz16Y;7*k z`$8=mCX}lcHTYmJArxY(wLgWZm1~_=LLoAwY%;+my9eYtVHb&Bj3Am?cz}>Dd5HdH2nOi7O zy$0!#3YHUx)7o;sx`vp&3YNTd z^D>b&bJsEuEqbvMXweF=7RW{pN^HpGMKDtCF@z-*MmRu)Efc<7khJmvgQ=U8m)Ows z;R!OGIfT{W!hscKy+pVIDRTxVYNS<)7HQ2(d7g}=3;5Y$y_f%k7K@=zPCmS4IYrHlI>HA7-of{pJ4+O#v9VwIo(KGSInY@Y!%Xp@~c zW+e)arN5L)NLn0Rl*jjiV9gfmej7&2*;r=Q3ohgpJrt+ZNRZgsn5k>2Q{toY82_1g zWR!++@Z4^=ON>UG5J>W*Qf_hqF=p(M9<$}0tA@?EtJo?>Y#{DPeqwZOG6 zmK`5c0c`-UmfZjt6S@5osCI-0+jpeJVuO${5FksL_Wbi15|t@odk{SyFU`}yYNZ(X zC36ES)=Xzh(4a4U=}SrXCOW!;_?M&SQyEmzjnkCW6jP?F*xu5(I1Q`qD7?HJeMI#1u_kMJqGpZb z6)U;f=%3Ji-grZ?U}ugz@X-EUkYo9{HOTv8jyeGYyFZaL5?!=Hq~(%V9CH%Ut$(#)5JB}D9Hq@wqYezmOV+-?$g9n zVJe`tN+afLg*)F%!}?yq{Ys*sAE=#oe61QK5!%B}sNxR2RBB<#R4s;`RBGX7sD8nr z+eXb)TDNgjD`9GEXHl+^-rH1>; zBB)nu=V-W8czxh1^WoJ%&SXWAkFgrHL@|V|YkB*Q^@_uzcaW&sWfCrXhN!tCg%xWg zrglh~s*a&+1xDn6a(5{5t$@asJxsBoA=eylPTm1E_;`~!)a2~=h@t*cB8Ww29;T-H;irqVc{jf$`aU^1Dfb)HqY05=0TkzCAEppaq3D!{;FA}B>e$SpTvOKSHv%f(XbAm=k zaV5y#koB$Y>i|X%#gv2 zX4!|Bn$}wCJM}Ae>WSNV3>ezj%Mc75d>fvtaXjTF%>dWx8zbdJy-~cFaWbZ6O4>ll}#$o z=JWXrZke8M_i;kq2D)CP7kd|7=;*iUXUB-HPjF*z4CM=$ z*?}t8K5NY%vWyoy)}CNCwn5lSrv(A}X;&lAr$qtFbFs;#XpE5`SjPq=I78cA>brP2 zOtQQKomu)PxPp?MH&7LAs>&cSMrh#5pn82xa9^e}{1G@`qEiSQz8}*?wI@g%mY57_kNtwoKx9Jo!aIDlR~i9!#;nmKOwK#1%qF8X6~hC zQB~3PM9KrhvP$KQ$sALn^WG^AURliU{N-hX+p0n@ z(dbKK*~w%rm-vijK>*Z zs?l|iZ^M!N3SvIy#|C0fUoif}?f|69X8SE(1)s=rX!#$#iXQs}I+uxuD9D1`a>_^> zMr)@dK<=u(`Z-VRD{+~W)q{lb5F$!AxC?A#6lzL{k+%x{t5Xvw{-P}XN;B0T2`kt8 zk3jdarHn0(m66g#w(EXHY;%fq;#uG0eKlvt@OjxSbOj$JIB|uY!3v4rY8 zIYbRZNaKNNlns-YzKdO+zX5MGMABI-Udpx|OQjTdAooMel-j)0_aiKgKcGBfSdMPy zsi_sNlZTxm+nnboOzUa%q^V_LiGBJS&vHe z{KYYNV)(T-CzqaBtvbbQ>6JIB53HE)DZoeDC_`qPK6u+lKwsp}-4n3?@LGSC-kz?# zem=h1YkT}jW94Txx?fQK(9H~!4BNfiAUjY{)5BWWA9n0)UYFk!8HuuHamr&fhT{MX9IXZOjM^pCWv893G26j$ z9DyS$ZxUBQC*ZL+IMAQoUMvsJF4W(ekYAh=Ic;Y~qQ@FQ;?q+01g9d>kYbYp;khVE zn0JqgI3bVI;ap0^2p)2T#9eTN5smVSxIp}l8AOYuKA!6B-#JHZnXllD<|tpyp?Mi2 zayxU`a{(Jz(u`kl%vj)-OVn+2_C^AY5mz`RxN*i2u0BvkMkratgjf{mur(YEL_Hzz0+0E9?3XsPIWD zC0LRsSkl?F7M25J;ExryGcXp@xqED^TWzdcZf)S4VN&I%llJHY4jM`3K931=(LNdv zDeYf}G_HEVlRJk=KlZ3ySE3`pKREIk>`_ofrT8Y2fu@ur6bqC3N@a$POgn~^kL!0P z3p1x?NK?+FZ%wydIJwI4FApU~A`65+xsmVLDg9r=QXC0ZvEmw~8f)%v7Z3OEYFH|- zfz(*qVIFIgPAFbfFQF8U5q(Mb)DeAI_g0a5(0@nd-d@ycS^CLdAS zQUtjo$yCj!KmuB=8MIPsRxXrJ%KeQ=kEI{x+o+9XU=xwpkYS(&QIc%10byn<<&Oa$ z6%>~@2%pAC)vI#&s!aGo3sJF3uGs(^FfHN$j%Y&~V4GHZDUm-}u20otwQxigNvE#q z(mt_88v4FsThOuk?%R-NY8@{Ez@&~%XmEZ6QuEa8RKsOIA24OX7mdFw(Daqp%>Kh` zS`vj`3@#@s+_Mm=DTQB=?7LO9m99jHrY(Vh` z-pBo}&ai0ji$ih$`nwjG*karB{g3uY65D|8W_+5`{%>+Vf~-;7(c0cC)RVZx9GbU1 zwGhQT9D!md1n6Y1Iu*{K<_SX^)$2jJBVX$4lEx}sQw{FAfcJh3)I4DN1M7}SH|@)> zjH6$JC6deIHM=~?7EqJZ{an}HEvw>jk`A7YYJ5>TN5l%ru|^g5jDH>lrxfvw-g&bK zKVfp^OrgKLE{jPZN$)VPcR3WSxTpHP&JJv9{ZLSht?`MYh52FyoiDQ+#%9U{6c)(oMHhs0FB}=>bfe8%j*g$a?7Xa zZF%$~2Oh?@lF8S^J!7dHjM?XeR@kKx$#2AH%pxswl}rD*@ALhQOo>+5Vdt6r>-Hty z*beG)uTK(6y_R=zo<-|yvuc`SO6e?{UJz4moSEU^<+6qMTDpRk*=c@G>q-b?|}<_y&B`jEIlJFC?` z5Ac6OeMIq-ipsy;8}9!j&*fj$S$|97_?w&LpU#kf*gO6U^^rFHr+zY6MOz+O5aqL^ z)pj*my4_v4Od}8d42Vdr9~w=;xH=pG$y+JmY=By_254Z zc!652CCgQlzQoqXsp&X%*K8vA0JG@WnuDnga|J5_iZ|^B0W9VS`J|o{N@k~AFQm*q@()3(U>yo;k!9fktoC0ADhx8)@ zmvl7K+3IQHvc&HD=Zz!B5WzftmA7{=n%^O9Vo{AdfL*4)(HSD;o1(A~*BPh^;3GkC$x{|Y@4)nw*w3POl#|G^CMYrZD-i1V zb`AdBoE}9-m>+eyipsUC90Sa9*-_fa4}kNAHc|7iV&7wf^~HG$g(e2ir2qkX~@a{viNp)}i{QaYsgL)f#@;t_5IF|`X*7I5YO z#7)kCD2Ri+pNj84xnRI=YUzE|$x~-dS`p!{O#FzRA@Av%MrLt^H2=W7@)DU4PMoxs zhv-VI<6Gog;#YI`A9!yE!FL1FyI##i;o zgc{&S=p*VBWaR?~^+G=p3c+QSeNVqJ3ckk&Ys^H{y8Fqr)eZ|HwJCRR3!IIG{_ zD8;vG=D)$we?2B_|Mr;J+d13YnEpS?V`UF#7gJkNCnseWCsRY)e`;tFZRHmgP)5Jp zF4W3Y=YE`Ha3H!eX6y?ru3^p*5W3p`#LODzMf7uG~HdIdFB8kIFdtlQXCf54Od7Y{VMuJ`-onog**o zglm5KJm!Z0Ab6OV&*7o6W;BU;!f5)4wdI4bA*xt2P>?jpP)hjCg^QGd!nsxapjObZ zavM@Gy|d;N+P);oCSUcO5@TsW#pa5!rgWzwE_l4jb<997tC(Sm$_=Ca)rmcw#0;~^ z3>VUGfDttr?6}S&p=Oj8T@iBxh(-)+*%M-zS{?o4HUJ1b+=o(6!i+ees>XG2^l*CB z=gxyk?je6Y>_5DU>lRyw^AO`}^@RgS34(EHM)TZaLiOx&iT;rY!funy;VT7Eh<1`; zJ1HDk4HrvcK}#4h|8`Qtq$8|9UfKt}^7$(ugLDfiNeofnD{+g4SwWCaa}?J!hSf7H z48vg3?y}WiD^$hxVmn4Mwo0;!Dv`7{p%A5#g}MZvAbR)|$&6{qq|6gZ#cK^BiLNg- z9Z|Pvz{JbXcI46ezPm;)6w@-bp}VFsV*g&;5h2g=fBL(UE3yImnB}`JHU0-^|9?@J z@~$rbvo2-r7X=VPcA~S}SVPbj&?FX-@&q>KI+~6^gytYfq!C4-CW^^Nw@GSnxbMO7 z`Jv$i_xm78KqMUgg8YOrT5;T3q7r(kV}3i&HV3RcmG0Q@2o7)oRAlzb95FD5-SUEe()Bcvf+pgWWvxU2_+x<^& z!2^G|!MF7$2yPjUX~&=|BzljA2jC5#^x#JLxcaH%BsXI^y>-Hz52B;t2eBcdRxwkU zFu)kbcgclDftkX&opmj1sAcLMYQM+Rnx#!c#>j%UvY-t6N)O^3 z4=W)}HU!v)On+rZdq&`OT%8rc#vJZzustorW0)B)Gj7-L0fo00#2NN5KJnk#KA-MK zkdMI>*Gn0PZt?$Wzpuqq@E6)ibcZt72UzfmcG^Zn8Tnqpu2D-l%~_~Y0^JZE0Lv+t zM9XjlHlmz4OZ{kej~(9^qVdk6FtIdbr0vRfQPU%44@$cFNN3O|Ly(HdQHyez$a&_R zEM-hkGTQ}JexplwoJC$P8L0pWz#xbGO<`HAX1ZTL}%%pxstj;TDS`4NggN?Mw z^hrnFW)`h0>IA|^JAlbpb0D3`brsc)(_q6<>W7mVX(NNr$IRt^K#+*1^v=9;iQSPB zih)MEBV=fa&E#*FN$rK{fU>N#oZM7|-!A0jJRR$GvWvfx391*22I^pL3W7oo(@7zt zm7ln9ZKSbN+7#FHkY@sV%%En>&0U=(-C*=WXTBd$d8Z+sQl}C6?p} z(i{0NI;1_!9>)aHnDNMOTzM-LN3^+o(pk!ny)}HLH6;m`nhtPB7quH)M76zP~V}D2-I_^oYZ&O^JV%u=}W{c7|^u0{@5cFUZItrT+#sgzVU>ic<&CH&xkYUpU zVTL#(tjkrkVHqqM;&Mntn5F0tHK!*eZkx`q8&Y-}k#5Z{h%Cg3D)5lguU`!GJM~&s zklleoVEREYN=fyvo)5xJPrJ5xm zQz|KnCz6y%kW=f!C)m)Gutr5BD32mRSS))B_fnk-%5Ih_&lgdE0K2$4F!1`1Z$Smk z#$V+w3b=_gHtMS6S|upMokgW08mSUHou7Fxl-bk!uiy_27V>ti3c$11MS z&MS$m@^!FWaduFC#S`gK7MwtitT`v~j7sI0sCtK%e1senXRGODL#Qp~ccWGSTadk_ zS$%QBDI@0^Zq0>lxPdRxggn;}at99h)460#O6nXPj_sP>Z>t9EE2& z=T|eNBvcC$%ewM%U&cR%_!v`mgnECFAGRF(`uiddm-uuW{OBjGErZX-UOZ9A!OXpy?IrwyF)#nAQCh-?LZ`&_$_L}(q zRvwOBVMC`|TT45H8f>4-ZIbRxsC68aKR52Ty7=#Hnq_u~0m2JgRF|zD4xRAIj*Z}< zK4~G=MVLwHgoeEPgSc(V$1zY3&s{IRgx3S`T^!$=I@(4f@jV@DUHlG*9qF$h_DA*J zha%s)-33bUQCdR1UnqUaMn80(yfi#}Z}(E&McCew(a9rjDXtOA{biI9@5Zz zANHU1-UaO*+F0Zv))Vn~j)oWYc!=3ORsv7FUx+_RrXTdW2*`U+2XpTd(COd41+)A8 z`n>L{hC1pe9k$uDn;k1|b~ZNlY6`Yc!?J25b{F4@6+AhN*PGkeM9PVnmS*hEjE8>y z;DRL+>ywe+OvNZ&)zq8XE!A4>q9YzqJt&e^WPWLu&dv{;S60dGKncMv zn;NB?Er^j6FPp6wosS)LcxTm{2BT9F_T2-NcLuqLQbExQk(g{mp1t`B7>rMAvOZ!C z;Pj1Rg#1QVY7~+Uk|n{g<&Ju$p%DMjsur1zj2nhH{;# z7uAb}T=JFB+TGGEtJSRoh7)r|4{LFF#HNZ0Gdku&GSowg_PyJtPuSYk5T+SvuAZJL zG}owRLrvHeQldv>pz4XJM0YO$W}_p(xx^8ZpvF++SykAA5GMJ@gx`13v^>4Ys zCe=0!d=UsrcU6=aWf7rJjxJ+Lmo9!C65vDQBPJV>a@#OeFaNPf8xe#T)9WSU86>*Q z7D*{cd#g^}G}Bc{^jeu`o@Kc0mm&=^HI=iWVHaN0v@Xe|4E}KgctHV0eDE7dx=f{< zi%>`c1?N>!|5TnOd|Xq3EZj4A^{2%ETSfz|6!tu;)P5lY5rU?KoM85&Jn^}V6f!m> zLNE}q73f< z1AVCNfnsCL_W4yY$#iE_*wrc9P7_SLI2nqZ54sLdRK#-)M2Paw-?nu~^?CFA7J%dN zztpMK7Yzs}-ll4^`TKe^Vq!8BBm!gX;Pn=7*}G~8K^W&G;FsqD^@97poOoe3>xG3e zE{=+Nt=ljZVeRv@2l;PH2AUGzS{W{v=fIT*M{gZrQ+JhM?e~cwN>0`AWtZT@u0w+fe3gnSfkG=rO)7X#(hpAJpMXKk=A>5GUd0 zIDvSJ=9T|IXPqc|WK5*`+|ShhrbsP;s!KiBPtP|1;pR8c_O~M@=wlN4vBAoQaLP_B z;!s_(HK4#3F%sc`1FP{Ztl-ONOhLdBE!o^6M_B*|@+2YI4qM+ePjv8<8+f!bxTIQ? zoJx5BV2{8hOnuRlltXJEcE%<-vxxy1g2aL54dro*No75TVm`v$lhl=iC&Zo0h-57* zG?WjP++(TcmnbJ{%OR}_giJ#*V;93XqYxR0{hI>SRFsYqSyT3nI=Ql3_7L?t`Cb@4>4rrRwuFwsV+Glnc= z>a)Oyd*skwwR*{VnXQ6x4%#0|sJ^jrKi0GawTC;J~aOJj`(<&tc+T{oseR ztQ4UqZ?1ARDhRDeq$PEzb!-Tc;Sk)ZE-ly((1A3YQd!8xJZY-KYvqP$#9JhI9@^WD z$GzX?Etm5cKJ6efx4Yj^Yp7LVJMOTYwp}v}g4HJ2lL~oXHreke8Z>5< zc9I@Dm2aHEf zejLmG=%uUb?u~S(vda^3d$yc|j$_>wBDpz7-b9C7bCgbv>U15`!KK({GG&fsl>_E^mgLqvr{A+8f# zOno*e{HC$P-nV@Xnrv5X2DSi6Tw$_QJF8Ob9%4;`gPtC`bBuOWfAm22eMMcB-T^lM zWm{T7l{hF6gPmrAhfmZWoMNt-qJ$!mQVLU4Q$SH}Fi;~Tv@WYw9kHCs!$yka>}mv0 ze999>q1HoAo+UdL`Y3y}ne3DAXE)_EYn)iwmg;hni3U_IvsVDR*Al0tQv#K90yh3( zO_kIgp-$39%rp;W==leU2)2r`s0J|eTJ!0HbHnR89p4y8Dw>WXxT!iM0!<={(L!sTxT9WZx* z*%1`MU!mG^hu?&&*7Z$-|1d!-%c8;pYoV4V&+09(#JNdK!2HNqpG@!GZ*Dar#C3p( z+yE5~4pIY}hJOW*2#&zjvRdMbLnws1$u2E{O z@{od#F*Yhc;TTCpOY#J(f22a0;&GSk;kuoH^#EsX)r)aVvZNX*w@fQ2F-EK0Y^Sk9 zFW1zlF)NI}uTe8yD?8o=QjJN=fX-w=uaG(!x$sC8wI~Xa`BfSJA-@m|jd5y@EYbbA z2wj#u$#Fuc)4IQSh!DG#xiHTG_G^{_qjAlk?30EQq=zOtQBh%grh99DsUYYLhg|QN z{{t>7K2+FPS22t0An*z&8Bs?1dC)bi%u9YEc5*1Tyzc02@2J$*ImNHGdT%Piv_`V# zBaPk3?<;*=c%q?CH4#i**l9YS+d7SIaZlqqhur`JLpnt&oXjsiL=K!?%^BX?b#^Myj!3%|Pbmjq^oV^W_8j)nR39L(5n?&`5X?yG6}t z%(6PyL8o#!1L)ZXi;*94YvZM)^yU^D;y5s+ps764O7kyzF(hkEOxHCqw-uo3I?>ES zW9z{|w*Qo?6))I+4$q{jvyG|D5zI`y)01tve-=6aF z9vtQ+8h!^EzI}jxTFp%s>=gA~A9;ljA1P$Mhsj(q_qOl!KmbAML8i|EUp!d2tOTU zyc;H*&j(kS^&JNGZvQ&e!Ttnyo`mn@IJ(~RJKam+!t(j3zcR1}>XwSjxJXTc!V4+a zv|ViGtVEiS#wRpNsd$MI?GTu3h?v>i^7Ij%l<~v1ape*Hbkj(P=k-h!(&CT743me| zT-@mP?f(I=q4~c*X@19d8GeIo|9eW}-{1EB@y=8%ob25UjciPn{+_b-U%3j=s^3VP zDu%DzSVP^m6eS8I(eFgEBHOB!+90J3pky`>EQAcD>Pa(5X<@d;bqLRSO#7@m@)zi~ zYIe!ds2TTz;IHA{JU(;wqI?==IkTs%ho0F7-|T1J*Na?zKXLe)-SQAe6P7N2OH_82 z2_`Bt7K*lVL$p?vt&~u1s$-QY>5qUA=C(?~L3AoKRv3wn5HBY;eNnaHgb-piW1rjfqhqf)6!XL1_i5jA_4 zgaLK-8fyouyg`w&m}^bOs8PD>yd6+1qSxyNg3yLAVa#T?=`_TJfN>*qbb&hitMM3K zrt*AUE_l}>U24)!NGcSL$gllhRhfwjVA`{zStZfyr zyXH|>EwNO*;4+umd5*5!wv+BbXO-?lnL4j^ZANPakZ`{0f?9TmwH9;>&0=P$?x5=U z5~F`47g8Du2Ha#ApO8{~6D%scE6Q!ydX}CsZe|+=gQr#p`4*TFZtPw1<9+ix4`;-_ zJEe;^u^?DWN5N$WwSRa_(?Hp_=W=6&?q8@SF+?=Dn+;H;mk?QA&3Rllmrx|1ZhrWU z^B9-@2YP^Hq-?jxZ(!#sBnMAUrE2r)e zkT)K3iKyaOav^MfU!`;Ky9M`7_2MS^CzlocLcBr^?_j*cTuB}-HPmB)gg(v2YbTxV z!iT$`K;<}IZ>N7a>RI2sCjBbVjz~Cs*&&7wG70sBP1;7-0XR4?KWoe4fctK=d3=Uc z9aKv)5PBVu`#0aMbo`~moMe9O$-xV_qu)59y5lF>0_Qp>YmmMI#h>5;ck}H5=kg%^ zwPDiU8N~7X!wC$Sc^|U4d+B3INIpN@q6M@#&ZfZ4^4tmdoUnki=7ZKE7;Oe60rpfj z%XYt62O5|pOIo~^g&|GNXd)LQM$}je_=Fv3ey`UL-$9GB^vvRK4BU z<9)%u$4?;Q2sLW=Cmz3v&FXF3sz>$~3$0(2yt~)zi{C3HAF(=vkq775KiWoA@`m`v zcN~i!_k|}hZ+?lW`mgawomHh&JPu2ODi5*L_DVdoLLO;t|PA>16Y-zp|N!w3ycq&0g=7FQ~NCe@f=1NRwER(xpjAN*2Y!Q zqw&KcIBtyNMpA+?w9gt_vBa_9G$p7gC{qwYYSKVHpqsSjEaHpQOvW2W7uM+!EVyTC z%*cRgstF^y+)Ql5FnOubZO{>#IlVg9X`LmVz~{G-Fm=WXAl>M6^5}{}k0$dX*$zwfao((5B*RuS#leJ3 zokg~$sB+CCna{B3a(mY^_YhmhvaPrj^0`fof^&@Y09`t4HIInw7mMSojLONZ{#~tD z1{fz?)NYCLwjevy6Gy>e@KZ&G$^vL~)v3Mw2>CmSyaR}(t`nuFaINwo^983Q2Z3Q8 zG!>qaf(UXNzQKe5Q`B5z(SCHQo-8& zMXoPMGRO@}^RzQ`R$vPeJQ6llSu6gp7A`ATghU5j9EUoI@WatBB2npIKgx>>s< z-Vpmsk3iPlQxdz65@ui51Y#XwuT4WWMv6qAs7u8?JBq<#;ljep-#sr|?#edbgRD9A*EX47FvspMFACvV!i9&h1f0U! z0|mx#I2lCqXNYwl{OogL$;-k*X@{SJCX~RVd=HFMU3@ft;Z1YSET!dLFjZIDVbk}Y zj&=OX5G#$d@cs9zFIN(twag9DB@pv?B+)&B+UKx}vQ`x_G35T{$R2E0b|0_An-}i( znOdg>#NARN)JrQYf)_*+?znko?%5@w(W|I!W%F8tCB0xOJObR~q!D@}miL_Evufxr z6Pg88q6GpSu{MWrb--uhN2 zx9Z#rP`0(F!PiRxlFHkmqTCV*mk zGT;g;i1HbmpfDs9nJ!?^hu`-^kcJ^%Tu*-#s!^GlIzn#@-oMo8;mM^-ZV2WPCUMi& zUQ}|Ot}2O$u}xcshWWspwBgD)qp|v}I>VqsWy-|cBHh+cAWJ)y6xqn$0Atevp9go( zV!x)dilb+!P*Yr+%6#_fV%E|m0_W?*u}hc)4c%_j9@ovKO@o?3f@6HU+9uYC#5q}0 z(iBp%>Rw@|N^Vzb)%9x^z|3093D+u1)IMU1g?LbRjU_wC)UBFPX=zqGleyl>WzW&H zKw>4r;V{ntd%`5F?`;#mZ(0SF?Px#4;NkmwhN)G0!znv*Nuxtef5}F8?N`xGIc5;@ zjae!yWzGz>-h0+lO!GNHGw|ItN@QFA2->_qnH$sgJ9tHR>49;#Rf~Qr=h3Ef=ISL` zD<<6&)aTMv3jq04Pu4ApH6VLa%D`Z_;l=`Pa2=1*f-OpArb3NU3J{$+6PzQHK2sMN z=^Apt{a3yaZEXz$&LvQ$mae9w$2D_BiRpt`xtX?oE0pMPvDT!h_43MV6k&;&XUb`j zK=HuH^^gpxF$XD@YCj=QB>`0iGDq1i%M6I4azA!ku>lHWhzh!EZ9zHZW|_(>Af2i$ zKoWG85N{|GYQjCz(AykC`v~VU+n<01^rWszKwL%;6d8sbK&H^Kr5kgVoQB(t>1-!z z9Bm;JJ8~_Ss2E?Yx66wv;%XQ6LzwB)W!d%D$%`LXYtq^i>VSQ8By0_~7O!$)*ic>R ztyh_&&1e$_om-$M($=^xf_?P(jgk@RsMXboqURL&4+)GB1h|Ds4w;nMD{-z%a|Qws z$-)#L3~>TqV4?i0^}=753^A^kymEd>RorBhoxI_5(Gx-6yYAv#H=?0ImLWESZu~De z(O*IFU!ZcZ~nDQMZVM4%i9+|di9aqEgx0BkiEqXvU2-V- zy-8TETA%Cxtqt;Hk%m`)x53u`5y$ke&Rd56)CT`|S2=YLZ#KAnjlbatEGee_Wy`utV?D{@B9mvo-SQjN(Sw3hy+rNpftha}XHe zg(7MbJl(F`HGXM}k_T~6@F%0Oeko$`BM6e;<0EwL&EqA&%SF@XDceQBQ@Uk<19uSV zCw5oq2k{oG;-gj*4}8JlqgR6u8#NYsfM&u&pU_viCH2-+z(anD4PX8ND!<$2&Qrci z$wLdEhuG7@Bsa-Qx{C^e%nsSMxfsvRq~Wz>!2+bN(-}G$h4yI2mj0G8-(uog5L<}p z>?%eYP_dM_XxB;nrMonj5Wpl{HjQA#%;u_jjMx2bUB=)a`rhP4+<)AS**RIKED8A; zshpnFEfdh%Vb%zuAGj`89U($x%yQJZ31gjTO`gS&=;_&LS3Km#KcCB}u93~@^nLnA zG8-um7(KuDRn)0JlSEf#Gclvwt?N=&%*v&ynd{7+kJAz7uyLbbht$#W~ z##^$}3>K*6ZM*c6h*XAZ&`r=)V@8y&wW{X;AJy1z_Cr8difx>-F|pkx z6`%Rq1sTlC^g+56=Ab${y?AgZ$0QOMS;LR;DIsMB-NP-OuUsu(`y!#K#*Q!#ILJ~G z2=3QqzMc|M)a#Aw7%4P%Y?I1W1xZm%8>961IvEQMaHx|QuQ~cAUd^U^P+OuTGMJM* zs7?IBMz@^5yrppMSD5@j5|(F8I-eaURsA%E`2W8Jgcrxrt-2OxmG-z{Qo@2jNyq2axlUPE~z} z9c3VCUNRm_rtu}rn`~H5d6)QcMfQPLzbm?~H(6eW#ZP2%MmWGrM!Wqz3Y>UXf-%0fqDi3;P-(H_-R+D(?vV z33mk%-!670-->Jq+KLxg-^zWrmu`l8EkBZx zG7wb1z6++$vVHl1f!sTEO#8AI>d%t>8+YXqewBA+15arC$`|xsCfsjdZ@YD6?Zf|z zuXkY1G-{VdXJXsN#Kse2V%whBwlT47+qP}n$rIb=nSJ)DI{Vx8)>pNv?mw{ZT77l* z)lHm*rBucvEk?{ARK|kUuHlF&~ ztgV{vE>nU@`cEd8LuV#-k|IGjfTM0G_W<*+;bSfWy%~>aB)vy2WPug8@yL$Lb{vMiae=fCMo#IfB1`TC5HytBB?xl?KqW{R94jfH|K)3;0E6IJ*MsME>@ws>HlD-#$)>?!Hey z;$Bww1WAatXTi>xQ*^TAQ|4nJa|REH%p0bZrO2ghgFTrgb1DqGmT@WwLeLi3?VpN* z6T>gB{i!y)MPy7*IwkaJY<4s*MH5l#T!dPSJ)Ld5=<;_LIr<4wpt4$!CeD3Or4K&j zSoSQ5g%&eAU>PTSGlEKniObsuwm?yVhKT@9`X}b%yNP9YLaBk@L}NW);z%Piu?t#_ zFhGxizVdklCRLMSgt`Tdu}b6~ld;S1c^R_tH{p|oKZwa9WYZtQ_5qfrKR+8_+;+Up z&?nA3M3mm_+KQEjVN1(k&ByJ9ha{s)<7NwDNiB3JQMdxbq1?e2-_{8+paSRvZkc4l z0=+h1cd3TO<-;rM-+out`}~mK6k$=wO*_xG7cr!(8?O=!mmMZ?_<1jD(2W`Wlkij1 zE>b{i26j#=^{*rqi3~Nt#b@e}DJ&X9OT2&9@a*y)=;UP8I!ibt~+Q4XonpV27xd_NFCGuqWI5$qXdi+A4WYY`Lrfn#{#iJ7E50N@ad-jqa>^iCdX zjBAg}7^YzA&~#_&hHDSe8X=zRQoWsDDZm-m$bj*A4|$i5?4b+%b!LHGUM-zxT9L1_($P~m7}Vw_mY?x zXDR8-*O8Nth=9|&h|9*)JWL4{COs@K_%fqvK=l$k5Re=*a*81hlMY=WlP?)Gc6o*9 zJCf0=pidNy---!lTjnUS{j~qQ7F#*U1@Bkzr?iLJR_)g(OU$zOh>7VyB)a8J44R%s zmAH2t;oX#zA2zDG*Jqs;8jvA+@qc zk*YFr8nXuf;^ty%3-3j-r8D1LU;nq)SNG|xwY90MfTzEkZI0LOAJd#Yo>v!I-|mmj z$_AicXCrLVs>I<`1hThw=^-)7zyh~XxrUhAn_9w{-F<4lzKNLfmqJ1ua_<*3e!_d8 zy$>I7AmPKS9LMy=oRDvzKj#rxNtiRRz?WG;Vw&^35BriI&hUC`%rh|HPMB@nR8za^ zNPDkB*nKpx^7@Gjcas4J34a@387?Tw#f>L*2 zRyZSxJvL`u$e4(YJ*VW+S+U}M$|9T4dV+GZZT<`1GUT_h)H(7`;~MOetaTgGvS*{= zkcUO!s=Q!2TZmy8Xn^L6t=nlkqrv~CroBAc;rF6A8*Txr+x>#tOROcI9W07 zYL$w`(3&QQhhud}z7v{?2&px%8r4e?;yQ{`;`TZNXPJU_KpLfa)bnoAXJaP_Ao-1+O(cmRj_D)Q@s8g#nlp zSF6EgmQaJIm~6Dc)fO&(MqA|nwo&r?fdpMX?=CJmkqgg>j7#?F6zAiS*F8^S*;FaL zD~nx0Za~Fr?u;Cmv&d$<8w7rtems)~x7ZrTym>WNkYV z5LfqhKoF)9DGjsOhVn@@5U+dyh46UIu2d3FfZT89=yLtc@`3s1p-W9e|vx0un^lFAAVH;8Yv+$lG}N{vM5=NT9WINsoANsN~fcFk$GC~ zG`@3ok?^crjZ^b$RBJ8P2V|-#;s;M}vM116d$s4`Wh35v#yx#+H>#!y}Ka0xff2!sNW7v-@xI z!Sf1oA6L}tTAEh|u`3`t!jQ)IB_-6o8oiAxW&1&yeXMeuLNDW6D`R) z(!cT_zwmnG7 zQtt+%X{%#59&S^G$Ep2|5XskW{lCz1JSd{2aaA+ zRFg}0{C3yY?G;@ki1?g4qYvY0Fm+~!q2d~{P6_Q!0dP2n9$!i*d{Xu=uvk$5ASAdG zr`-^ritj?b;ol>BFhe1d)$PSwuF_!U(C4O%6s>d(ZYv&+Pl$}qB&rZN#q@GFj#H%@ z9G-Jv?NJou@()p+vPQcsG|!ZiCNxs*oL(Je&LF2w5&MAIGq zML*G+952hl2IS)+#semz=JlsEq5?VNflvj3e_oxffMOUl#mH9(Glqxm$~q!S7!?kG zGOTNVp&tCHHfV~f3r-L5{n=z{{)ki6Bf;%x)@unho1)9&-C zqNCLnU8UJvRLLP{8katp-Z=;1KK?Br`Yi$eg;{jD13ql2v)@z?sI$i>J@oSk_k#f^ z0Ji;;@RJq?Nhscgi?P3&rb zm@p&(x|Y4Du)V6)fTIC7eJFk^2DiP$ML&8SZi4~h=i-`Fk0F=wxL7Ha7*(Gk zz6)N-Rq?zYg{=Jjv6lHZ-DqcLa!{tdP)%%u&U6(k!$gt1^6^QyIEu>j?2^7gK;^+k z{l5_tfvG+7N`)|8XykWl#v-jvPT!qD^h2)f1BPAUc93!mD`zgE_*dRu>ZMaRtOezo z>ni&;Z8L_w#)A;@bG{_ppExX94hz7Q!i0EK!Z-MT@nJe?I^h`qU=}m#|Ablpn-BYs z5~aAZjP(E10+Q6Az0j7hzkAkXT;ge0B;|$e)WL-iFo+E#k`rqse%1bB(g^)Smt|#w zxOq{{jVf5ExuF^3p(VGdh(6R2fOI2Rg>hZb6ryt5ecjO9EZAfFG)lfElD@-WyZv7M zymh@n@wC~1ilH1fZ$LZnDslq^(WP$$<{PNnGZFd>Opr0tA6^?X)0B74SOI^Y@53ZReg zWxiu?^CMG?I%y4CBGrukrDg!IYbM-;hBKJA>w(s|+sLt)^p=`SbDi}Hzgb};?*{YR z^F*T#Eeh2det-pgn}6C@@H|>SW}EE51ZZF%S=KzWR$ExwT3PVP_5yXPsugsQ0XR<1 zSYdXo>NJ0rIy^u@AftNeF5tl$!4x8&n?26Fr>f{G(zVhx)tZ`bb%#4ZhTt`M@?KSh z&W|S2y|;(q-gpCq4FX|D@~7s0M>=U!$^QwA3MH36K@b}d&GIX<8g@|ZM?EJ$!LsEj zZ+dugGKbDtA96xq2n(fEL)GUi@lz#5TLvfhGz`m?imLdEJqjNTSgO#8@RI7%ZQ@_H zjNLYLnz})l>(y+Y@1#Rg_#23bFp|JrSRn1yU6@{EOsX@Ca_}eE$ZIW9+Xi0-4UJqV z0;l_nW25*j>ITO_#WIuO*C#A!op6i=6rK*-JPI#?i@RYTk%XnTkf4R*wgD+3g$h4y zPN=|pX!mbJjrZK?_Sie=g-;9Z^QRz`^mSf06J|PrpJb#69DFIdMHW2OB56#^BTN-5 zoXj9B6ArP*6Fe@BP)f=qn##GMc0w_I3Jx7mh9m4J4M07Y58NG?M5HDI)fq2}tpY3)`Ank%QrP{XA7YtvotHJ=bG;loQuC+jVd_@stpqVr znG?e_Or{)`+nh;}BOp${>TJ|3a)9&(?~-U&aDD3eIcO+D`Ge#aE=(}qM#Wgxn1-ol zUdeYPNWxVlZrZ)wcN0pI6MHDqG;@(L>L5nzur1h6>)dc zssQNfQ@9}bD%DpXZ%WyceKnVlP$i1p?Dg5R+bT0lWe&NdW@Ff{DS{!5LkH*S+g5G( z?%-ef9!Hg==z4-tXZrNkfISC5GY7iqDB~W0UQJJLXR@10))*7b>Yq9%?d#dy4oC~(Ly+l<7n)W2z`B{w`0yC?A zx{)pSDUkFN0P}nVH-%L#BShA)x#M1_ly?B7Frsg2S9PA4b6^M4oX$v@;=wAJFlRDz0_aCp11;h{ zesXhBPzzX1ZcEs{wM0f}&(R$Bq9}sR*|%_Qaj$lv)~N#nu-#7|uvI->tSv1$O3ZcJ zwIGvxRB9}!Pbt`tDj$=AQ)&})#W2m7h=~h`vJ758Q1x;^xG93C1#n^wchvD^SmNJD<64`-pBCNBTET_75iLEjp^hF z&gqx(+I!StB-}mlIorkCg-WACbCg-#^4Z`Gp$1|1VkEg|l2*f;$F(NU>X;nfpZuny zekFQeB_TWBoddJnOXd#$0w?7dS5c;0c8)cX4pTl_uZrO zcko8$B>G{vY9_e|<&7)PPmcz^BNNSD^5V9OLKC~t8FyRcbS<45W@ZJ> zfGpLF9tXeiyGx|QeXwQ$YC_YZn4%9|bu}a-ZkXo0+Rb|SW`~eYc##a5F^YK)Mi_JF z_{af;0CT%sJQ$4m>YvE{s|odUe<@$sRpD+IyPSUs+qv=AQ-7D|yS7MFW! z;tx2P$P=H-8bAv>QZ}*46ji0d+TX*BH%|vVo%H6|okh`wyk+ zLNwExJnSu~CQz8Ibp5pP4jJ|M0|$iNTGkcd`)+#j0rgbWcFNd7ShaEMn2OaMvQSjn z`agr5T0l_pl6nY)e!lY$mU>nkEf&8!xMG9vPK~rfzw%Ee23$^-kjrWvbpy$4K<1k` z2&2GVGTxraF%o;pFxOl=>{9WlMS@{IAZDY}rPEL9mW_U(q*WVAn^@GaD>blUkX4&5dKi%Z z6L-)-o5o3-VOhVR6^1=Ds#Sp}Fk{fLRfFfR)*x)FN@vK%AZDx1#Ss2H@hYv%2*xSp zCRvkV1=@gLBg970Lak6chN~2}ea-{aRSLm~?5R5s(dF>p`4+d;$`>xQ_(_Zpv=VvS z;y2kAy7jE^9EHamvMlN3*>mFMFqT`#bq#S%d>?^iRLJjANkGa-*`a()Zux4^QFxYf z!pcBcM^D#gsY3@v8Gg2Ld^gx$Rq$6y=WOIT%t8p+{FOjT(-8D=_htNKJura zLh=SoAbZf?4JKsIJTcDG6sXa9i^oePXULSz4qr*0lo`nTmq47x-ouXklD5*MScris z3i&9SP&#Z%wna;d?p8r8g{_czKr2o*u0X6%4d?!EXR5n9ld_fJ3+HZm^_0}%bVOPX zrcw&#{Xl0bxI5FMI}_}^0gpQqygO5rQ#+K27 zJE$#C=Aut)kEoN*WFH?m)Is9+JP7Wlw;72zIPfBWF)X%+>qQ$iK%ofEmxObhb})3T z_(M<8_f=9erUo*Ez+9-j(CE~mA=XWnlbfX_Q_6UxU@jjy$m!x6Sxf9)Jc1(C`L)27@Q@o}Zk-bz7}g?7~J>3mXKJuSWT*FeW5yPhZB3VaS!c!MhpACv-ARlK={^u{ytX<#j1U+Jkelx{GfBl>_$?K$> z{W;rn`I~lz;q#3~t^maPE*lDA_fYa;gr?m~C`C=kjV{2i;nM?ZCz2@A_B4jw>D7ho z3(l7SxZz5&>VF-4(*ghXEC;@@7pBs@E8>3@lU@(Ju&B=jxn^u_C!BQfip z(En)S`xe#sWR2`uBJ{-^-U+!yB>pf2gT0ZH{GK8HkP3A821Mor-P$I-xPF9;zsTu) zNv(Zpr|1RVOi=&pdvkiVUApxxzv+2ts^^%>35X5l^KKuY`B_$q(gWrM9sgJP#GH~= z-&=KFIZPfp1A(BbF9wtUs0(MOtq4^A@PAFV$HSJBE);dop$a z!L_&Mwv)RRHx5_X+D@7ybvBY^6?F^=d;6CvBjih)d|>}XSR)!QUit9CsegK{?eeyAcQY(8O?#x831zz$ z<}W8Vf2sso`+^bu*5dpI$|M!(B%e`z$nn(3!%rIl3a4vd z$Ayn2^Spr_t^}28gS0#^byB~nsx&UQtdsCLQBl#$PKP;*wi0ua#&OJfG`{O$eOS65 zWAa3z_;MSV_t(WH#$o=4(c4~G*R8gqZ%4*g|DX82H1@c|<-)9p<2MLEw92ipf%GXr zMz&Y+_Tl%)urg1Mp4Rd$H-hdIhbMWd!@^slm)!7Lp&Yp#2|06H{f~^%xYb;P?EDPG zwNt^E2#&saiA=5R?NUrx_m&Nxiiv>VdgIaex*`e8mg&<-v_4K>VlR#NUbF)3jwV4zmeLvBJ8Dg71B5S8X`f@6TS?+*pN5%Kt#;Jc(md4LY_wz&?{{O*^WA^Ov81#1 z=tVSQJXXLPRC08loMc{^qE{+~tgSj>ldiT74z*Mtf)c&U~qnX0Q> z3#VY?WL(%xQM=hbS+iI>hEtC2Cpc{c?X!zl)E^>rttij0^7%>rrRf>zo@0@{WwfW% z!a%~ofiJ-s)2B$U>b&Z1$Wn^5wu(P0zO)d zR$rk&#EyphjHvsp``VcaIFxo5v!b$1Bt=~^uG-1UJ}ukTVmDMIU|05pFch)sW0%Q5 z18S-#z4U=ZST|?$d4YIFbwSU5ohY?J^G0JR+J->3O=F3MMyP3bTq3O#NXySXXrqiw zs`AuAn`tdK_UigVbTL}<1efH;D}N_Z8d2J5e*;RbP;_P{wsv(Me3qgd`e<}+6;E+4 zT8;1xIt}s(I_(%lXp^MnZ1{oCPm}EvGK!Y|LqqR0pfZWeZ1kgLZ|jl4zvSw<6Ii1l zG8$v_y86Pc_@%ey_5>Mv+Yu)eiD9M6=DPk|e3s>*JF(I^dD@H#qf6~GDEPJZjb_8Y zL4wTMb_~{8Y^t^N@y+hw&w_Yn!Rfo6S?@aPFW<(ov_Cbl$CvhE01JU&REYH zeWSET+X2u4w5MnW%{B#foLN+}Z>)|TNS>JXo<;4ei}r7hSwRfM=KAs}6@aIl*a%t; zhU-5l-xx~%WSILlQthp~SXYQ+5K?S=f z0xP8P*NC~nEoc6v_F&15Y0<0jb+97Z4$~2@SpzN4dUzeu$UKd6L?epDD56TqQualQ z*RVo6l&Cz3rcVRVbioZOLl&N#HHzt~rc&L>R5{|C43jTXK*Rq8vk8MC|Ajk8Pu4C= z@6xI^AkFdGro#xbH;nxQMCK~QXZ&I?ID3c~9*Ed+bauqiqZ5Y<1nV7Q4{94#n8|AoWjcN=KIb@|| zzX^uHUa82Kwg`0`Ssy#@4-?->ldA8o zXG%~E-dkDideS%T;Lc(gT<~@oYssQ{r~SlmiITra%))VL{B2livoGK;o_vqyF?=_{n1X zSj6n^5m>b0=(sH%K0H*m*gAEls5}djCDZQivlZ=AzP`9+y>})p9&C_&l_X>_i=A$k zim*J*s^J<88?HB8WeW<7$1B6-zNLmWj3ra>FKWC_=WS~p=IP%(hI#dtzz2@|?BMsd zt;T?s_IB8TR||vj;cNcj~P!1lkP(@*%2!!^VdRJh+ICb9FDrd6G!Rq3ig zv$RFnSLXWQH}MR#BWbucKT6iE3HZjo`aHNngW|vI7YNvljzNPlIK{#nKf=GFnvH*#~cEf8Wp-cVk9gTb2`)Eng~p`J+2@ zz7Q2~qvokafUxStF$#?}HR|Z4h?C#62JH%jSRx}0>g%U5Z(5o;{q-=LNU?DX{5T7F zaS9OGhi`Z$yJ$zdmg<2uEZZemEBs7vl_iBDJSwwqWmB)MAHB#)lk@B9{|i03vB*Va?)Tpw%0 zTKM!S_K`#5-iZlAUFOOX%A7Jex=ubvG|WVYm)F+bm>L;`)ufeA1O`rNUHxo)8gi8y zlhv>3-upKz!*FMafC@l{-kG8Cc;2zIs(z&(UI~{&(q}iUG?=OGu zf(Sp@mY+|055RTBLdgl{Daa~{ciQsU#UPobPeJ&#UUqil2(1evn1ajUjiQntGm3bZ zC#s}!5Kbd%tv=X9FLY==%ta#t_JE`^{&6_bBwSlMv1}b?tEqaOWi}jg74RAr4E`d- zW0uP;XzWcki$C;+vOAW{Jp0*s8&vzns|@i8D57;q6yfLhn9#1K?()jLI1ITB^>qKM zC5G--1&>H{K6}QZSf3BO}73+_&zUsqiY3kc0-p%o?pW zyN^C-2!SoK!*d0=%866#82+*4dAomtGXw!p7Ok_l5{9|FcF#V}U(Bbb$Zj175SVgT@kAriZqH*(1zo#%OG)ODRc28xB8$%a6U?%h@C0QX4s)EXwOC@#VgX>3AcIdQo4xS?)R0j_LX3d*QTY@d1yHU zi^9KdK-k;BJ~WvJ5U0*aAqJ}b+4qxKL({|AxrUsh1A*kQIEmsm)NR)1LhdQvp=Hg= z0ff|m_0Z2EZNlQ}80q};gv=8(af-Ekiu`rG@H2|>{I?2`$)|i6)YAF?ekwBS=S1PHUlxE>=*H}Dh-`&U<$VX)_1ztnBTfPahPgA_ z*_@g@OodMBTzS}#{i)8jXHJ{MGcgakS;4<3% zJq#KB>*rld1rtPKq>IN~#JzIZram=GzKM=ow@_uIJl;W$^V}^gdm9{xFIp8F$M2{G zCv$VVN^ZGLg2w@uSpt_{b3|Htrs7)|@a7A!Vgn2Y+Y*(_#j6_{rjscV;h~ z7aZZ5-_*WRtRJ*4cq7xu7mL;|9oX$nnjx>qjah?5?-2`o^ax>dHD^w|o}rgmx-;oZiM=-@3}?AvGlTOiuVj*+f0LIlg0?ALzwn=9ACR7_T4dN3egg z7y{|_$eLeCR*x(C1#-YQ*x}80;Iz*xhLQE)`7SC3gt|Sv!g{^dF^8yjv^y^>8NX)k zJPmRCp5O%J$KvsKDW7?EFzmlLhVWnwuzSoJ!-7&*+!y%!@lpw#Mu^|Ggx+ZI4gp+0 zJIyrnV(i_sN%Ih-7AS$|AF+!HXW-e~?l+0Bht(LSk@qX_oWoUCWzQMdHuk1OIBp=^ z*sH2vQG0iu3L6&9CxvFYN$2oB(=H4#oRh4=JBnal6HHsKKl8f*X16+{_Jycfn;omJ zaqK%HuT5SQDhM~zIEJxLhx9YK^!pf0w(GA=sx|xC8Lar9a#52hKL2kQTy*I>tOe4K zA2|OUW&i(%_y5*G@&6NV@jo@tC`FzBie=_qNcO1%QAN821xJGwR0H1^3S*FgmSGDj z420Ahb>g-uS-Y|-23_|hi!Z(HBixN)-rlMbmz42J^E|%dHJ#?XoYC9yC5oc!q|f(T z;>kX^Z43qQ&=AEqAfK;>wjXm^9n~fY1rcAfravaAe#Yhg1dqB2*C)rb?e~xKOp7Um%Os&Ra zq~((IWJ7G*KyR+;%*wvJ$>Rb%7(I2(d)r`bC)U`)3z~C}-;3*fN`n=*^GQ5LQb@~y z2V+DB6Z3NGWw)y3c6s-43sQV$E0Uib7h+P*;!v3g%8WlI8b{NvnhVB=L>n6ZD-!d%+xLX#)rt|bMYuiKj4q-Jujtz zXMql59SmoAcs?g1?P%jA+Ec^;GUK%Wf|F+ac8~u1D6ZeY2q9OpyY$Fhc5K!?!G=vm z)Y)R6W|b2_m3T5aJV3UYzaWXh7_;#^7>IyzGHsVai5g#pEYY6$#wuFp9Ym5PFYkMe z(#{!ByvkAU0iM<_y#0-6bjcNO#NRv6NG}#Je-u4`Y#2^3LNp?b^+`)8>86$*uQs zyueTDm(_|Uh|nBuj$a};Isk3I0k4k zte`F&kgSvT$QN%=?pJ&c%xhY74rF$#&u2SS(2tuGpiJ5M;M$s^Yf@V=U4dM zXp_Y)W({yG-Yzru(>Yx?CoyQxR;DbV;1J=>)d^efPw5@krDZg9G`PFoh1E9G>*{%N zsDt0)z3uP%;^KhF@P{Gqq9{gngR_jTkPz2|o_^Y|`EA`C&I8d&GpTF#G}lHKo9b46 zKY7&WE>EVWjB~E%YN?U{_$5hvTD&<`XFJ*Ftxmi7fF=StjO$ zTZJd~iLJ88fnW96-?@9N6lXLSQXZ!G5Ik<>v1v>Vs?$rk9nFLv(alL>>$W=E4kZjZ z%e8fMF1STMw#-^1*0u!?*-WFLE$3;Cboe{{GXkG|)tA((<+qtBf@5-O6={Db*s>eC zx@*;KI`tKSPVlUeve)0TU7K6pNROwxgkgw{RN$1$9kW!cux@v5zS#A3&Ak_?s4 z+9}<2yR4Db0x2%N`gz*S6uP8j4=)$8bL7_@5H+I>i`9PE*hg1(VUI$E-`wedtTv4l zmsEN6oX+YNIhX%2$uh?9B5L8E?BA?k;n4Z?XH=Z;YJU)uhQQYU7yeZD>l+)#W=kzI zy1Q(kXbMNUsv=K0mDX9kh7o8URmc5eF<3Wl5tQNV{dQL05gT0v5gngUUj z{mQH}U_53i4Khg=`xLD%yH7}4gqL?P2~h)ISz#hvC$I7EJk`f)DaoLi7ozK3)O@&Z zK)2uSW`2ld(>3LJu&sGwa_%%mN{+4kaYb^D??>>x;lp(n;q%OQlD5GFz$%|W8mN+%Z!*19A@hv`)`$7it7tOox4AvHMHPg5(Zu| z0^b3}p&0cs1_SQ*lkwr9cGSWmrd1M9e_JqZ+(&e*(rSc<4cfdjl01hU*0wEjYHr8y z9-u5jszphg*B<5ufq`{M7@8(m2ccF6?K_D7g1UaIF$OP;8Ao6Y7sHrtOF5YY%ozV> z+Rny|IUQLLUvYJswO`=T3}S!xy60)850a08nYV-k+QI>^;efEgH!&zHQ4!U+r|Sgki@!Xj5bhorbT$noTAzF9pv1zpA7c5%WoxN;|LCbU{mkwRk1jwCV6+40<`<|!;U+!jwNFCnAUUT_QYYOz z6Lc-qRjbcC5SB8sUR5ep#oZW)1L(^Mso^>|#6kFyR?@nNk#yt5pU*3kXkig?bQ-vtZEXFd~e*|z#@jONUumHNV!biqdl_j z8)kMR#+Vq%w3QL&)mHn)%vZN7MzlXZ*|_}CGJjCrZBS-pjwHzC>fPd}CctO)k_Q~y zcv40Zyt2XE6?+o|v(at~Qz?|(n3E_L+?cagcrz{2;`*X@F&*Q^YN=F)mPDAU0tR_DKt$3*@H6HNmvYSm+#{FdJ`GdMp}&txL^-$5Z=|fGw4n>u^4m21yrIeQqm5pl{-l0k_7BpD>TbY ztsV8W1-CLfVZw!^Y0ObGKkPR0R2xWe(21Bb&9X))N!Qn~tak=2!6XqNrgdRKt-=c! zDx>3El{nHOe-ZU9kbF8t7SD5Kc-xC{r;}>^aVe-X0;6tNK9y`|BO}QWkP(j07+?}b z(6tUOGt!~OFoz$ZMy=AhWbCh}lZlfVGwb}z4WPAkK`_@zvxzH4Wb3dSzflo0Lz;Q3 zjyAap&prKA$3Bg8B}*q!*~69umsXj=pOS{7UPH1GlbVi-s|pUvAeqAQinNewET{*> zbI9Hk)pMdOJcRb3WT%!6QSQ_Wyl@O_&Gx|LTZ>y=2Ua>85>Ggps%Vk;@cH5fsV3uD zwfL9Brb-89{3##TM_YBE;{H&Fe}as;_^?va0dr557aU>tOiY)48n8j>+VR)&A@DO>))?#MQRQHflF=sk-hw>*seFlCApP4^LLVXq`jF#L)$ z1TAK*jAtJ<(8$?|%>etVLDki3X3?&SjgE+B9vUGCh{GsA+9~nWNXx+=J9;sv%ZLbE zc3yYG z!glH*-zJ-er9jv$j~W_~;17akxeD}GBJznoF2RUhwdc{8jDBt)y8z?Y8mE3WPRe1M z6%`$%G^K1UuP7pmzcH-P-8Y?3ugY0>!<|?S({({v>4Cly_f2IJZ)Za4telS7x})+@ zok?aHpU;$LN_Uy{yWB)WFTh?;wub1JD_O9xxrSiS0-Bc8FML>$=^Tv&`zGad3#7cP zomw@eWC2Z0?=~z~E%>!29M9bxFyy911M+tQ{8u?UmA(fZkM9n6vIE`wr$*A<<|h+E zbDC;Zvm0-JjVHi&UV-~?{hbb^BKD5{cVhX(B02eaHc z(4Ge}*hIL}A>XXfk0u%L`TedgG}4sMzswikA0@K4h4ew=M2|73yVBY{9}mO4CE zk60pPxF4q~m}*>7QkFcK?nA`+Ub>O74RLEwLf<6E3+0lTRY>aSBT zvPlvF`|{`#U}+3c&r zLl6=8Dx9XK$ZJ+SgoMCOHuN$a8OR=#B{WW|5M-bwS&sb`8XmtPg zt@Xy!MWX7pI<<4qteRz{~59n6GXSDW_u4IaV(7WpZ}`z4!|-_zg(-%b;cF zjZq!lPq7SsfN)7V13JoaVl@{CdJ5u@*ULHj8YSpNpA|BLq~|{hu`VB2PJ`U9UT@WgSDWsU$KK%FpWd3qJo{F-H7eH~=k+pr(DFtVu`|wg=f5TH0D*>kFXaJ zM&un;oV%5_C{eU%T>r!q4nxM*?UL}Jd$LD810|EbB-R$KHFnv_zZ4a(jYkcr0`W3;i4!|+DmqKZ&MIf_QB*j+P=rt+WI^(qVr6Zw!mZoMMF-g1H$<& zt??i;UNBLXT%DV6!W>y?W86qvo{!Z&D3arEQ-zUVXU8hs(88!XXYQhY@}mvAMXb-| z?f%!!0IPkQql5{tj=lgA{mRc1@{@K~_7Ql6G&3Q)83?-+LEvmZdFeFPn{Wg(<3p|4 z5EFi{m`EzpXt>up2iF;M@EK; zT++fmElR@EsGDnq)N^tiIrf3tmOp{p;S}8FI9tI?^{@wQr-__mxc%)P=%jXc%Q-?)?}Hu{(rk6O5A_H(hciT zIDwrQO73N&4k9vQs0mH6dMI(MM6DyNOCR6gjvyr19JSFks&ta6D@%p+M72b6Z`!)N zP)P4zj6h~?S-Lz$nV9MsyF9UVOe458FGLE=Bo!!$c$jOh{X{M-W*|JUrctZGx;QHu zXkUM1oYLN>wN@r5`Bxa=9PIWXvHC*g1~hm3z)lyRA))7ml;e>Tc>vKJ&?8Fi33Pjx zYLC?&hJA$@?ybNAt(oh3;NlhSDb882m4AcxK+g8~yItHf%l*LFg*Xp6DMm;g69N<& z*hd8(gMA$TI43>5{n16is~h+P7xOMn@Jti)E~3rMWA51#&CV|xtZpSv29Goh#AL|p{p%;rNV8raG$Tr{r3d)Z73UX9T za)gf02@N_C(gS-+@(&z<^w6*0L;K6p2hg(>lC;2ck~?^%a?Nta&>rt=iBWw!rT#yR zy#;I}&AKFN7&FtDnVFfHnR(32%;PaLGrP?mGcz;WZDxDS%sgKI=j_?Ly1H-mq*AJs z%F3)#XJu!75nn{u@+9m<={&nn8J(KW@Z(&9ADhK!_UNzVG1%bbatVxf^xXup%Pi<< zxjzh;O9$Z&(t~Y6t)WD*KvHvX6x8r|WizaAGk#g!;MDub88ykZ4a#v*69^XtZGLPH zy!`^s#T$P=*aASf0x?YvnBGLv-z-&ooH5umv7#fgjRu`uQ$(s#%4|D_;NYi~HL_)H z>%S!Q*eMj6_e^QO3RfHM14xSkj8f2SjGb)rtOWqOeh7!(yiqH$gyYc#Uiax+39wTMlixrCJl}vui6CHPkV%(e+4;lhb zWhz7r4!c?Wja zkYgS4;=6PQyZmmO@JCN% zP+w)RSc}c)np-?#RGs8@^P^FYai3)LnFoWno7j|&Ia7(HgbG_VIhtB#7@a!k=u%>u z-u%20>9#Yyj&-Pij%AXkny6-9#dhP#kYS9nH{{|NSwj{roBXs1RPCutOb1)2zb03j>%3+*+i_UZA1 z>tictz#j0F7*~WgNt9a_ht9m+Bx3zceIc1il=y^%yqYR-cQ0trvsWMt>m1&9lAQz1l4ILR+*iUzVUyxI7$)Hhfs_A zcOQrQ6!;drT%0PYcNFW&jk>tv6ERfnj;&N{a7+5(g-_8W|MYCxh9ZuMNGSf+yBS9H zI;$D^&mHR6i_L|c6dotdUZWQtH<+M{`!sg^g>5=2$D)YumJ>LltsSXs_W0mm)Bi-y z6sPlpYitqrS$?eUk?c4l6ls7i?E$}d%J;MOS>6V0J|GonivfFr9w80}*xmngl|Wg$ z?;jU@$TiRPj=uyK)!T9n(UsMDv$sNUYg-RbmL4DGnP!%5Qy>@6v*xS+r^eN73d#=D z@_@s#gG|2VjL>*DpQz^6E$!mA<+g9gKSI4qdb zm4u-Lewkz3RX_8wvKAO<|9z7Z$@QurAfcD*)QJ|7%+a_^jf+Ul{TjRe>`ll(ezp2c z?vOKYzBGQ=tIJ6y?{$fpXomsxvmm%-bn8&+nGDd<^oqER_==D2_>jqy4LIhG%mwgd zi#|;_c_3Y+PL`P-%2LqjYmgl>!=1zH7LNu_vk5mj@C;6 z@_2$`)FOMHyIU7UU)KZnsQ$x{5arQ3Vw0E8J%aoZ{DaaAuOz~8sjwfPJ_D()E5{$}*o*#ThtRBT0=6t<|@y^feE_qafc>JO^3LouCl zJ!VX?|Cd{tHLa}%^smmi)AojhWT*p}pLNU}~C7>78Oes}6!ixV=s zy5wtWGG9oT0G#4re3ky5k|f7pDc|D4SojQJy?p`6vkc_#deTf_M?^5()xh_%qv!;$nfmcGfo&_uZ)X8nIae7WEYFMK$x)osN1(x~ zB_m&;qDr-Ry}@b?>7#Ml9?LSw*b!A+-3j_dsby|CP1c7rOH)laf_*Si`78RETyI?b zTZ@`jV>~kDU#B$tZAbBUg+oUt5Wqh;xTDWb0Td-(%^m)N+{;40Z3x>4G0?jUT%`O6 z{KMpxdc>dN^ZbPwBb_tu9rir(hd+Hr^8++L&x}$m^U9(xik+S(dbd5_U!G>Ap)8Hf z^#yY}%^7omvIH9^0M$no>=qc-56WNSvvpWi3=nmp6vE(uwBSwumw(wQ$FFBwrDA}9 zSS$VC@!kIpmi~{a%W_}wT_bxVi~q>5r~IScw}}7QZ8~5}ffk5CiZUb&A&TnWH78^z ziU>moLq?2M7BY6ZKDBc`$s@_7V^itbxU^)@yb`ozJy)StEC1`}xL{{h$H)3=X{(C= z@0p-ofP;DcswwA-lE7|P#`b&l(+B=l3*+CTh*ISU_2;x`1-vo|DEkygUG$a-v2M~x zB>;!!P z7LA?o=!0i#^tdsuNE2l~2H$5Lbi+7fwn%2|eJ+9_G*k*xy+~d%KI&a+Y|R}Zbj7F- z>^fi4NQcM|`(0p5aD{`cCr9Mb;9CThg2at4I-``Qa=$FRMU#hXv%UP|mF?Nd<$1fg zje|{qrsC7|bhRmzC|#Q_?i^~UxjC9&bye0XJA9K}>Pc(n38H1_2=*WM+!ikx7q3jC zlcIJTPV$M0-;53pCL=_mo9I=g9u~UwO*M?qO30_Dm#5pSth00L>x)|`zLn$NnK`+a zHZ!yKxz1zD=~H*9fR5CznmAms2HVgS>4|BI#?DlnLzsbagJ#~{KM!IKFtpU^EgGC| zPu8L1p()_-2^_TYlDT<6`K^nC(ALcw1Z+L67L`oq{MB|(qzDubEf1_|>ZY{D`Yblp z31_2y_^^)RVeD#qE;C8l8HUan9teHskuCI@4xW%$ql8PchqJc28pVn0ZgkEuyC@?k z(S%H}&&-r{Y?^0-+lF%s$e|lmldP(aY`H}U+jfi+IU&mK@$4isF8&F99szv6Q#|yf zQ0+pNBy}<(tK+8MN^k>-&h>qgd({jpCR&S1nX@iq5_0sCPPPo+ZCKexGE^&Q%q-uY zSn22zj8{~KgW3?c%8TDsXUr7GzsbR4W=GP*lj!gloOB$w zwI>`ib>X!%G0iJmthg*J5iHcu$>>@`VDq0rz~B4XjYFS_BP6G9$lK0GN1GjadfPbE zb2(r!*1kBp>I%qQ@GV$*nYRx#f1v)B#lSteDTd)5A1Oyly@+yu+pV~_4ccv_h8nGA z;+%AJrxrSMF^r0Ev5yJiRKnOL;bmPEypeTHiN9A_NX z^~pAO6(qP+(3q>`+oM2Qbw0`DnctP#CLW6Zs_P=kCQH8<{813zdO! zfB>%VY8ETmyL8`|hG8fKD7}NOmwg8hB}Z{9jg>)qhpf`WjBVOkr<{bT)09S!P`K`9 z!A{L{mey7wSepVlf5l;&Mr^i1xlviv3<@Tww|3w5O`J5Q`GpYt(OXC6W3U&ym=cDc zLTTp8m5ti9VyA+UO#>gPe8_PH_NH8v%+tD5RC^G^+}R#H?%Kq7rG^ATmSSes>8ya; zl4pqJC8?S0Ar>92lHVQmROqLuKok`BYb1`#xV zpF%+Lvz5RfGry+d78h(}-=;5BpEx*0Cn*yxe;EO^e%3=skzj_?9p_3~1a87z*KPGC zkKgOw2oQkDA|kG(aAB)vhdn-vv^ShdtUVX2pjv)GW z11T<|x>&>GX3#x-fMH+?JAGD*6=EJaWAhBYwUey!I`V*tR*I`v8a}iIJmTGP$-cSw zFdkRsh;Wb&YmlEaU0x3Vce%4Fb8qJg5-3hKxu?U=ECLI35z{#HEG-E^WnNIJvJrNz zI9S&?T?mFg$nb!ux`Z;iA_FnKF_LP}QS~@^aiCb>n!cDFe0hC(@_WuO>Y9x-FM?8| zeR0dTi|EISVdh#u_YO$aov$(2x}`c8q+XQpT4fzRZ@4V;M&n3a!QQv;b8Gh? z)|5)zSW(dhry0XZyX$;6OZQwfID}zrW@glVAg>7qQEyHZ1cRu2VuJmIeKLHJ>Rfxe zHE?u0Io)@A(s*IJJl?Uh2GURfA3?rY(MFjyO$8zzuFzA{{cnHzN|=GsB33x!7bS-) zR)BOvCuo&FOe86B=-CkoR6@?4s8QGG1uuVkLce!0+&LSNGj9dL`Bej|2EjQ1u$@JuMYN7dNJ_&rn!F_?;`x#I|qPjOMI z)`S%(!kPBMTx=P;f4Wt~C_w|EPVqRrucf2>N2mAGT)ASjH5a~r z`A0C0Mi`EDNT_nQuEvZVr=o$C&8cC9h4)uq`NWcZwWU4IPm+0(PG1$r0= zN#M>t9(@|}JX_xXwb4W?wEc$j<@3t(wb}eHZs`ACB7y3Ehy_Rbkg5aad^RgEYVwdG`bMP_vK+DnIg35hM2=t;4TDIA%Acd6MN#eQ z`8r0N2nPinwBjodfxl|>-ISH|^nm)B!rC_;)BG;pNx8f(Xo;J}gwxq=Pq}v=IiK85 z9t0UjpRVUt&I_i{lLh zVah^_@4cP36XFlXn+=cWh3$j3tEqyk1tiZ)!>_~ZRy6lLoKo;`<^y;6nPui9_{e`e zNz?O|M$vybDevwNd+z%$%FpSfWv^S=+;=F(H^}ANO5a!Z&s$uAsL%WP0I!pl=;)=1 z%yn!G+YNl|Shiu(h^^siSDMsg{Q)W zh-`-WU$V%_6Fljnx55)GPC%!NNrcv`uf*lrfv&K%!et~&W)E{}cr(()Nn{C!Wc!}- zw1T_jo-S!Q)+Yla&Pn$Q5nw(hpEL7bLf8S_HUHK-v7A4F4bWht_&uQbV7{b!bMEp! z^PrvLu(m|awnci5vCnw1_s5Ckiy zSb0w=JaAkwYXzxdEviA2cag#af!zHD8EUFS^hB$(j+7@IPsU9$YmlGsAo9`#y*All zbr~ODY9tY6gHB4`vy;CbYO(DNKnjiOUCi4Kmor$_q_OVPH3!6*iaB;jK@$RUEooB3^7+0Vo^VMQKS9CLHQFIt2CexH^(A8&ig`_7 z2sTsPG^c=-}_st%(}m4$1cn#_q$_x`4O zK~3`d_DQt+lT^Zqr?sii^)^To2E}) zCjP(8pS+z@nUG~_n1H|7-%|(3I_N4_&Mc@$bj zc`fQLo3(%Y4A3oY@!1I76qzt77(7+d~UeK z#O3`JyJDir-B>Cq$j!AJkCV*EYfE3Bfd}^U(Zw0w6|w`~ltCwsJdSqG)B(}o}-@P-i zFyeInzJd5a)CERxoKS+ikP!JgNXCEawiBP-a*H*g8XuE&#SQB6TV_D z!{vgU+87dPPsWibidKdF>EVEHkiLzN=YaG8@ z7>lm&^kkdMxHnWdRXNLPxDx41=kj9JPHxb4rNSmcVt zf-QE1YNCuOIF#a9%aV(+wAas!Bv-AwmvXnr_T14jhx@_S!iySLgNDx-3u4V?AowEB zp}CjEyTsnYbUOm6i;CA&nLhVvKIP+%0uzRAu2OyO0V; z%*L^jdMsUGETQXjM&Ya1T@AojY;twAJ?*tPYo9vfFuq5U+!Iw6c#X1Y(e){$&WM&I z-I$iO$)cQu^28?Dv|=8HwaKDL#+JTAWURQzo-OqoQ`3PTh1svB51bv>;m~rhm5W4C zQd_s~TEkJ4nBSW+aOmn3;5H?I_E{KnD_1i$ohM{Irxd-&!)I_;Te7m_Ez!eeEB~XzBHdc=LU-Ce@1RXaMYL zVb#{c78gR24~3OmarR#e?j17oP9NCvPl58X`BZCBqVp zaC8?rdV%$+2y0;>brh(f2)hU!p|c%!-KbqbmCE^n$~oo@e5RNzF$Wew>AXuc3pmdB z8dAb19srRrxBU_0L61EWs}VT?T85iF9DNSC`ck}@wAF+D(m-^GO{RD>ZNW`Vco|L` zQ@9=J(@=Q*+LzgSML{$2b<*-SoW8iS2v}^nqjM8Vhk@c=kjwXy9anQy^iJ=I;WZ`# z-+F5or$VKtITNcggr%RZD6B#=5}cKYjZqXts+^t&p(yRrI8)nmJco zaLxR>Q+Leun2fLy)dYxWnuK|y6!^dtyjAbdUd$DyQUh6dffGU>-^w)c0H``@RU(UP z1zRAennEAL%oTBosEbhOgiXcP=kz;muNXp$F$=e2zdc67DrC|pSiZayE7hXyDV)>B zPz-6x?3v+Nfn`OIlSZQnvKR5T0&?D<7E+d^v-WPqND~Km+8WWU>4pg;`d~eNP_X09w9%f9ORR)S@Q1pm8 z{h;*pR-$cIwD*mE`7u8^PJeRHS^2mu;M_N633XsMB5~zkBsC^%G)nMaxX)gKd~p>l zFB08j3cUuOGv##o6iQF5kbb%&H$vghN|0Zy;n`K-M3Yn?-A*p#u}X`Akd z2F|Aq3W?rwhg8Qgs`YlqhZ0xFov$RP?~`%KJQ&GQd(==gJuQkj%n$=k*) z2LiY%ROR&3I<3qGecV@;GT+v3aS=?7Rtb|^C`Ja@w5nrfQ&EkW&l9+|2hwC%SUs^s zqbupA|AnRIx8vm_N~sMiUvQZ6DBxxDs~yW6Zcv$>ySCIX2&@*VB&k|D+jarn5VQbl zd+pQaeyK(&G?dip2JO0DRyy8Rvqg|(i(69qEx+D~Xc{lXoFgf*?{7E;S^) zyV&OA%k#ykYdw^gDf4&zX*}>7TP>xmps5jvi;0Waf|H{{lcOGO^c% zO#BYZo+a@rDqPGD6eU5(Q?q|x5mN6lw;cFNuH?YwKIdN*MU&hW)eQsG75bQ~`)63S z0$Y9H1#t~;T;%3UtbU{6S>e(ba9RqIEkff8h4-&cE9Ee&ruPajhkno(pgzr8wf-~G zzba1)FC`om0W44B9tVXVI+#@~`i(qcQSHmZqG%}{y5^(e`)m3EF>80g72I+kw3m%E z5(4~Kj}NDd>HyR!^I(05HN@F!_s+988;_49OK)Dc5utSiosFAn!RpNi#ljQ(MSl1- zgINi@tJkldKFKn-)HXSPx|3RR@@_qhGzuV0 zzcl}?&FcK0@xJc-VcE20HSOnL#E^~c0L_lSt?tW)0QV2}w(Q5F_!}JccEh{^{ZIOP zQ0g+>+|%!&XTjNRMssL9C=M#!Z}c>dJvn!ZMj7CnBCq*;vt1$fDZOOlH&kDsseh}2 ztB~}^n8oNYb+W^A`DnTWOwU8IKoHx^*_7(*q#-^ym_77!7+I%ebF)@IW_p!=m0c|g zSl2JGVPn~#D2y(xxhVCZsFMNbjZKjN>I~*Fx+FQV_HL0$6OI*(UJj*lr*;V4l3Fv^ ze~R7`vd89gt+%CWh)1v=S*5}FsVhC>u~jWY29UN;wYnw2^0McW^(MHsb^5*T;Ong9 z=K>KQmhQFydUxa%wpyRkV7s)Hb&Mra0|;98i1d5gT24?O)>^`BRChJwtI_#2Yv#Ar zAA*SIo4s3{K9!*fQ5?53r=%FXdMFeHB6@R^= zr(hX2d#1Qi#uw;(ZZ(N}H?~nSb)7Pd^&m3L_z6(Gh#RrX9#hip<0@Ut)3A2iWsv`- z{Tcl$Uj+R1!Or9bdeT&w4LQ`iNo{1nbs5W$U^rr<0XFiY;$SFeX)PmKBV01uuweX! zWF|Utyh-wcJ`E8()JLKIUN-XcB-Fn=X~7b@Fi=Fh-cqebnW$@Dm!*7L;G&eG;$XQ0 zDD+>%J3^6du*<^$RQgNn#R~>UCsTM}@&`1WFc~A^l6+dcEYj? z0-R?Uou_|1RbbNrD2jl=ML4-{zcc#;DBU*2U!En^sNGU>XVnieTq+e-d6VO2)&5|M zXoqPBsc4iZtJ9dJ$Y>Y;>r}>!?+Mx20Qk(r|0_|C)X@Ky2X^P^d~}#{vl%R0$~xi zz`afYXh2jZsE(e?9HAOC>7`)8%5d_!pZO<)R9zek)A-9WBT@SQj#d44Q1t&R=|$^* z$Et=jUsUl%F#h7Wd!#Np!x1AVB!JET;xg@}0wWo;3PRTEAZpY~WbLi}U4Oo_s{bt^ zWx$yPmG_By#2xiOX-|(OQka=T;e3$9{qil1O#KDUS?!=qjoWLJ6}}-X*tCsC?ZQ>D zhtn|N>i8*B)4<0a;-qZ&O5YRuh>VYJw~OI?_)V|TcPm6>(qiZ186<#@WLFuJo^7(L7 zp0`fZ@px!#g#X&ptraHqNm(EIdaTK0CWtbd{rF`RWl_}MU#zb#8i_}N!6DOwB68!Z zg~3B%CQlGWgQH1Y_&bPrdx0Q~8B@KrF~_K3#3JR(eU^S)T%g-2P92CAO9P7E6h``} z6XI8KO?`F{zNsPYA(a9_^3vG5V1?mU!F&su=9bu}+$6v7hdXqWAZohznA(hPP0z{j z7H8;3rd&=tt3YpwYXEGvu4uIhm#ZpK3#9IhBSl=>M4TYu6PEf;3H zgpx^>zdEKtNtxa{OLxm2KD;I2MB9lFVCa z0NMFkxZVSJ_4bXUkagne6yDTkR8K|FN|9e4C9+}e7KQnSG47Rh*PhGg=`8pN6-EVZ zX|`6_aM+7W2WTfi{6A(u@(MGAu!P$4VHd6erFImWfk(F#e|0K18npJrlJZR48Iz%y zAkRR-H8S6sn5%tpPJ{j^^g#6D)U%JD#I)Umw~K@-ic(ZHzAt9hOwz?EaMeQv*Y)F? zn)^y((gzcQ#-q}7rM@32t#16XWSytOaW`n8_|jv@8Hka=`HpT=N_~z1YAkhsQtJ(3w$r|cmNZPUAy_?{a|D-snNAPiP!eOl7!qwlIG&Q{LZ#HS@7A>nCY|hg zjK6{c&OPFs9fhD-O^Zj%!;6$Pz+lD!7dSJtvk*q$cf1&8?|1x1R5{TKpM^ZDLK!dx ztFje045n{H5bWfXQt%es)llhoZ2$Fg@mB>~wOI$L+D%278+fVtg$T>um^stym?Vq1 z)_hlM`r&#xT*cH)XEyIg3#4eJ#R;+1jJ!=tzu6n8P{WKnTh~55k#1HH6Db7lNE@#U zb1sSeWY{9-Bel|)jQbv=kemI(65^9KI7So4+o80@8wWe)ah#khw?rC;=lG-$#WkU& zFGwM?&5hi(K(})<#s~fH$J2;m{qJ;~-m^A8@L9%+ z;IxNXSl)+Z9jm~(gLm>Fh`{~0Y(efR!EN_IF2#O|I)U(GF@aplLyrCd(T)I-L<5P% z0@*VCX^R-21WiN<@(lk|7_FDWR}rM`?}hBB>^Ep~5O{15$t3JQZypRrE+oAt-+mf_ z?`W|Efqhn!+e&})qyp__+>&t2gAf|EodL||8Jx&|^JGKZ;Znc?p+xw}5hPr)339#u zNEXOPA=G>v2??FIsmMyPvt{0kfQ+X&pce))?NB50%U4Dy^>m!Vv>J5Gf@Ny{+m{`h z|F2#N8-ZM6BxpIj%ZVGWBDXeaA88qB%}uv3~e zr;(M(@#OC0fD9^jslO@H2%J?#APL)c%7H>id zWaOepqr^xkv<9NDkyBJ5Qz*kDbA~l?WK_p5CXSV>R4wbZ$G)#7QXRnLy$4c#pkO?s zrKAYL=^!-9LwN09hy~g-W`5I^|5XJkEC4qr_q3L*kaB>m4mEb@R&i75S9(JCoY20(bFVrpKZD)nwLI24@$m;+FPAxzzwKQldvT zneJlO-8|%$7dGo6t>Ru_XC#G>HvrJ9+~thYrEwN@zQGL<+wS%iHjGxX~m4Z@JfroAx2|j1ndMsZ30z!mYVYXR8s2L!(zA)sVZ{`@i-Z$Gc zi+QAZhpsBmMAs=0W7kO|8bU*)n?8O=sik(Dh*v}|G zcw7;Yb2QrbTXboDG)R}O7A@GmmSM9FYsYS`X;;7Wz1Ws`VdZsMU->KBg)iH5M3v&T|t zXq@Aj<`r^*k^^04Fl-QD|JQdbhkG9Nv^w7(6k$0&$75NY?X@O<7ZtCuKHN`JX)?wv zwek|t`tqRh)_ynm>ECdl4sS2b3p5DG+V}t07V3YNf^o3;Kb*1{bsJS&bu52Gm{l@c znBea^GAOdO#Cr1@oxdy*3N%EiFw22wUm3$gwNV{#^M64<;P87|SamF5-Ke(Q-glx| zdG-mNeP>q5ztxkx)cP=7xp>XIT=(HQ-D&cX(l1(zEJy9{9 zt4J7@3B#mLc=;Pwz*J>Hf1=yvH=5ZHoUv3+aOCff=8`+o#S8fecKA+Y&7;bLiqX2h}If&5>D5t-jYKo0ax{ z$D89X{x@Y6f(v&~@txaptsJ&j1~E^G=KEhV;ajY{1`gXCMV!Lb%+3Xz@3ye^m7bW4 zO!x1E+@epyQgeyIXud;m^>z~)*SV2TH-V81dP>&?k?5~#zGNEn$CwMzun4lSt{4+L zuo`N~N*YE#)cZzgXtY%40vl-Lv}mRak_v)o1kTlcwKRNJ?rQeDc02Y3*Fx=8Hx(3I zyrdtM_oSK&N9a1JYP58P=ue>5y)P<(I8N}cIB{Qx1DzH5$2pP9+sXdYG&0l^14zdShEpY14w?k>W+E(n}yg1 z){XgTS?oymLILyM<=1O%2gqIMLtQox)u4UWrUU8=P&7T-bFB%PXeLL>ICu<881EjD-A_g0Y7wsl~=I)nUb<;Dm4=YYLf5R8Q}uI7U^M zDM9)=Ckk+9j9g)qNrm=~$WlQ~8#o%D&zkFCZh$&aPN5;eoJf>-03*XfeUC=!J`Dn7 z=!=10n?Kk70>Y12jr9!xP>A<*4OwAEpL(d9uKYsfvyS0KgJb(z*&9Dto2A=5X3bp=tfuX zX&e8*>T~IiD$HJ3C3K&|^7j0(Z=~L$?ffa-9#DW0uncUNSdNNRBo_DCAofC(wx)P2 zyXF^(0`Gtp6yBdfMN2I;)NR{3JrNIo67M9_Oj2`D7Oi;!U=5{T`HS5qy&@*|kS5r! z51b5*NC^l9Ym~te0?gi|C?DqUFf!(zd;aO2|0!eq7(xD5dH85!Ycti^BI|v6;v?tC%b)S+{qD*z zU?2+mvg}w$Zz!r2_>-fvXr~^QaL<{UdI}@uu!3#5$V)qu+NwldZv+7X1_N|*bm({X zX7XVwTaDCR#V_s?d`%|C9eqQ2*hb3R5H-2&*(58{)TvU5Wn)%NiV=w9@)us0g$YfF z!A7A4Qvpr3hwQd|3)xW2pCn2xCEJTLvql+~=I;9W?8Gg`+Wcven|j);i4+O%Xc>CL z(F>GptwcLnlQlSCQxS60>GV}KSRr11tOQtTqov7_GiG4lsrOe`Z&z0`5Bfx0^9Z^fuM@7XIaaR!lO3_oLaG~AS>c$e=v$H`9 zI6O{r15}Xg(Zm(Id=hMWz1j$jQh(NQd_1Ig!exmPeYFS9JViyJ9b{%&*anNZ!vq2W z_M4QmD!ShSRTWdRN6#uD8!AjAmnRU$Bur$R)rBewNi0U2M!RPl7rDh(aiC!J7d4Oc zANBb*uc0=GkQ{c|94w(2)8nix_PEamow9!lVIC2@(QN#aXnqPqm0i z*NpGhIFsZ6AM@LeR(3tc30`aTJL<30 zSjbezS@SmG??@wu+Xd%<1P;E&s8?89klU2>(7ZTh5E!`XF5hoP@Y=Uo%#$GB0!hQy zdecrwrFCg~J1kewv)D)B#0>uUmdjX1#ccE<;?~_Tta^gNDbnwkH}PmjnCHWk(=`4J za)-3AYfOJQkX}Kx!;CR=DuiWFY(?nD@O;iz%?S5v9pl;ffxpyj9oszuxKizd0&<=f zy$STZa1BqSsBu3N4z!SuNM2v$pP+y>B#azyi2f)K>78nLY#Bw=ArT$m^)#?>3`;G1->AuRha&+L(|M(W=fBPy2TluAtIzT&JI4!lS@RfIFG1up8Mo zd>CQLeuz>hdndZ>@lRPPyQI(l8_m#{tO>jO(dA`{87*6(0PL<(yq!Am6&MN~BVS3I z0Ux2nFnF^p;tS+U#iMHXI$2ZC#OCdis-uPL;N}?;2%M+cVUOYu&}13jwTz`alIxn@WZRc3sBHqee2g$ zPK3JIt zfJn(3Id}-B?<(;9awdNw91RBpyFVe}PK@ zx(OZU-bGe%Tb6`{t<`RRnod};i9);ibjQ-39#KmMea68Mjf6>t8Z1tt<%!cZ815d^ z2mI!HGk8M>US<<5^NTioAiQXnQn~izbA9oA()*7cnZ#ed4S7(Vjqf=P2Si-($jH4D zK)n8h^%F}GFp3h`2gL_WzYPC|VlZJ>ou6n_`mS$8H{wHY(h-T{ljMT2(T_T5_|HU{ z-|m-~0R;ge2mgOfl>hNB?rWm_?`ffVrm41!@ri#pcEGYjMFkC6ss5waN^V}uw-Fi( z8oWeEhz*BvAf%0_W~XNmdTUD!uAp6IL`va0? z#~S0u(%N?06H70XzHCav)v8b4wtrqw&*zncKO7QIT@g{F82MwLl2VupERUnW&ms{8 z8DaW)=P@aF4SzqGKqPaJHkq`pN0LEVUQmZoxxQYTKh10%nAxz)L>LC|Nm zM4L7mPLR{t(UcRSmyI8~MC8ilO3H&W6CvZAocW)w*8NTBp8LCBXAo!&c0@;S7h^Nl zc?r6Mc1_4KAL=I7yRgQ=uyZjccLv7JOj?C>q^jD^O@RO%Y?9HMinlB$dZ}NsEQ96x zTOgI$0-&Q2MR{+@!uodHcI@bYlv9zhSe4spb!ue;oZmZw;Am;Cg6TC^nZZ;%#o2s3 zz^jrsb4~VSNm|lR|Cyk0T+-)jYqgnAyMH}fkKVqNsNaTgyH@0JEfM? zqCL6D-O64}(TstK$!IKGGcJ}~PCx5VIbhr|Qn|SFj(QVFN1F6~rF}S}TRpJR?D|Lq zWoij)d0%6=Xf}(LVJ?$0Ys@)m@!>A9HvYHEnZ%6Q4~zw;z@>Va?XH3l%YGMwJixlP z{P(~J*^{;OOnsn@*6N-GAQeDwCaZb^#B~UBgbQhH)F>d~Ynd&+ziO1hc07!EsAAX_R<^1lqzcAAPG)Ne?s-1GCD+ZUXQJD%=ssG`Ws>YTh_OYC8Td#Ql)B~4-H-9B1Pn?^D%R(xsc zy3PY`W^!Y*`a9SBm3JKB0Wx*Llm)625ZZ*UzuHaECdO!Kh8No_imO9fK-tiq5jD)x z@IKNnf2Qg+KVatTHEUCBhsXO8F8&hiiJUAtU6YT;KX3O;Ro=V*Ufl~C+2a7+dNKO?Vsg^9Y$Q*tI><%o2{0h8SL}SY&{|9#;N=zv)i^6S_*Q7|NMQ3 z1fm}jPFTR65$ztscDS-^aIWyfKt$?!*6tZqHI-MQE_QQ1!!J%n0{}fN*i>5$xA|DJoFBT2`z}I@@C@KQyvz<1E&efsm>N=oHy5zMgFq62>g*)|fshVS9}JA7!(r;6~>FuZWNe z2{icO`DpAc9H~{{*c7(y!K=f!YeMTSNmABTI+|M5=UUlk6co^NOkX;2c1Uq(Kp|w3 zF`7aI6I&g`LLs7d9fnsO!VKJB5gMF2vdn9Sr>h8j_uRJ#fpBm5eXhH%sEfB`e#N(! zrzBe>B!fK=*>b2EWglRw#5KL)7$t zarREpnMC2XXm@Oz9ox3ivCWQc+qP{d9ozY1cWj$~?BwR`bMClfpRxCTxDWMSHCENP z=9)EueP-O*7P3@e@~m;&f@Nl>k;+O8`R&aTd>aHI*p3_>RI6Pvv-h_$C3 zScz434-MzxEGWBda7nrbikt67e+=~FWsBxCa1SGANb9J1O@?6F47SQ6lC?@q+&nVOearcSdqxy<`n~z`E>Z;wyGL z;mkk6?v>>3TOfJPVbVi{T;-NG}1fI_qxwXsU{kw zp~#dPxdCfOiI|8*i1VwTb=N_=Wh7<4Me|zn+36iQk8D?>Fv8E@FWJF$-TaS3QqF3| zz%L+(yw7vFx;`jfQA?QVhXP!KbYngFd~d$0tUL-jON^P^zyIAekZ z+Lal(T#@X+LWPW@7-diNJm)$wua#V=tfv|en4LDEqiHjKnI(n5>9@(+71czjgCZ%G zwZ3^oj)5m~8#Q%pO|$@Sd@BJ472gJK0NdP*U z@u_v#)-B%?ySBwbT`?I@AcY8R#bIsd7>(?SI`Qtr?+>DXh4whfsr%#Rhia;mHGUnP zhPw80{?eT^nwC`>(lI>{V*=-keGt-pkA!*;)pW@0G2kaXd@Iv+q&j}+#>BT>ZG^yR ziyPP6JIu{KreX;kMxAm=iuRdKL$~1l0kPS}=v_`=2tnRi*84Rnb1>_6Fl*GKXCfTatH5UF@yP=w~ry zan9Nhm^fo2a%PKF{tf1=D08tax#O+r_tpA~g(Sm+P#*@A2QLr@EYMj!R>uoULLtRD zjm$^jYO^*R<#M_8+~pj1`@YUBZ|9@#w_WzL5rU-O8vd~Jc)9G*K(Up1n~f<}uni|8 zQ()o0-?J&0?nuBGPZsPZNp_wHS8g+=Q<4XH9Es&y+bp8FMzwc+%-QuB=&Hh91$zlUrV1Mi1KTsA{mF4jo=;Al@bt zW$93ioKS7oP}@WAcHXt^s#d9tRE#>tQ9y83W|Eoc@={tll5YzOaCC z)^u`AT$Twc>5|yj&;oa<@b7s?JyBNh#I9PAmTHXIy;201a#p0NT$5~pH1al-8G=yX zoige3t>-2sHB04U{jsOL9Oq7K2)l4*Fw&nt*eO61x!+jBR09TY$f#{y!N26BvHIhK zVcRGR{KcTn>{A&%+Y%);2HsF?iLZwnf~7SQ>|xEwy#4QjcfSaK@ypQtIF=rT+Jebw z4Jxo?RDlb<0v`z?{yoi>jX|=civAO67{Fkwe>Vn116N|Z)^wS` zUPcZ-e_`r4fqp$*C%5edT#j&M@x(^CyN}Si3hwTJ z{=0~>p$Aw!u^iTb^vhJZKTE02^WrO) zaZl(pn@UKTNihIz+108TVe5-BSgze2hUeh&v@V_r)znar$S-o&Hpq{9o9E+}&yRLP zKWl+RWCEF4hLe)-2$5dHWM7HoPr>ZZLG)MH@V`b-g%GGX(>D#F@}Fpk|C>N1{4Yn4qlKNLh4cTZEzD8fRz_7v`=rkz%)K=vM0W?JZ&aJ(v+Z%Do z-99cr5`?q`o{=LGfCmx?y>b29^Hsqp!d+s*~E;^+1P4N4-#1ahQwi1kqF8 zo|XNXfTD27ZKn=acD-j7*Y|#xa5YQIXmO@?lY>BxU~8yqB5#%c5)>oiS;S?ZnB8fHe8QRrIM-IO#GI~ z=V70#{@vA*^2bMLeeDKSau6Xiwh8W_f}Dm7BiYiX%-VihS>+YucdPM9SJn2sWodWW zqa$kbSC(E5Ri>>S_?=pf(gG%&ZRR6>7AhW1urepUFyX6+JNSq&mSC^obuUU7W9SVM z3a^YH;x1_|xkp==5>#3p<*ZX5-xOlD?22&-L1ok!>6C^W-*a0yh!E~n~Dr+MrL&)t_!rQ@-gv;V>2P?>L9s8W) zu}l$BjbqA>o~?hYg7*=mXAaSB51crsh{%oUMJ%P+shU4W7WH>%DA$3wnFM)2j z0p9(svig~Uez;+`aV44Ezw+AgQHgY6W#{gAzh5(tqygQQ@8T=a=hoO>m=PcdJBImrK?u5fmZVYsbm(eE;4BgbmD>Crc@)GXc zqQ-=yk&X%IHrB53g>GT;Np_TB`~iECUAs4^oC_RiRoYAbGufBy6QPgjSYcL_80wkX z2flX{R|&*7QWb1*OVob^y@L{@({N&Oq4+`h8(0VuDRo8;Cu6o@oD8nnJ+VpmkJItt z*q&g&xx1-{jz9l{m@P}K4H){Kh{gZum(u@D%+mbV6VcAz#L?N~o02uPFmrJ|&I3))*{@!^Z>*u->8Mo$t8ijkI?_RnZ2Y@xUiC|GzXnD7r!VuUeLYo!MSj?Z>%y z-Nzdoyl?v?aRCryIL|{%#8Y^A8SpAT^;o(2^k|NZ#js#7K87Zz#gljFw21 zb>|B-p_cQN5~Eh2_h1+gP?73Gd}O5I#$lskA9e1z7&C96tmIzXVyk4|*5gZV&{G_O zmxhIpBJTL`CNS1Z6r17fIT2BRSl#>C%UhMe54da9a3$agl;`gGyN}D=Lv`C#vcJxJ zkDqa90m70;wU=3ndbUIzJf2(SZgbNkZdYJ)i$t6iSV-pL}M&f|+zGa{faw{He~@QEV^9tY>FqGbiVgz)oJ z`SbSJjDZioNnCJB=6#h2>EiN{V*XiF%k66z<1oa$Nm~TUL7u4O&x_{3Xc|+`Rji1R zai^F9YFNiaAjjW+opNiGiSV*&o`s#Uij@JJxpm4&ifd)cNcfzS?`0P)GVA!Y%8Z<; zmo2!jF{egLGgJ~}8_ipIkK9jItlfiSlO$4*CKoHT%5ZXy#&%wA%cnx$EPm2to)tGK zl!-Mw;0Alw)1JC}EJ(XVgr&ajcszn}_d|M#%FfI)Bj%h0rCGGgQJkX9S%ieIwLe9ym0}mDMdPR>{$ra4C8WI$Pe&RuDvzeD^~*ktRgfijCHCb2U-3 zR`6k4P1PhQ42vU|J)q?E@)8@k#<+Is`j`YOG2?p_^n!h zbY$PPM08-tRa0rBR^E@t^iFvB2C>vAmgY@}V+wwUNd$M+nq3Wv2Xmw@?|x(BNeYUoLYN9HYhJW$Ui3SM6M^l|<+nRew*CP=X z(14B`1q1iyyDnOUW_1q#CRo*^sfMpz&?SP5?Y^8uAc)&Ihssej1rL;Nz?_rI!q6s# zxB<%{1+v(!5ez(|Bsrjmy=*!g-(kEcCQ4*16hqGtqCPey;qS1$Rk&)vNCR0D+i)iG z@Pi$6Bra+_=g2+D^jF=}UsdwH_Pt%v^qc|77W)darL|EMV!SPBkuT8WcLF1+#{2{- zt#X?=}5^FixjdKffUGR@aV4ouQZ-l{Cvqj`1L{7uSL zO!3#iESgp2$*IAap=H2fOboJ~D`~r{8m*p5jp-5pdVWdYWX~xq*W@a4YMnnk(N-9q zcH`1x)BCpBdKyN8q#b-MOe;0RtjKf$bK$kjV+6pH*;*L(_|JG{9IC1^F;$UQF-KWt zR1Qk4_)^8cqd#5*`zGQirz9a$PcNH64S!1B&RS?bMU4ietIx>KIi1sW{zJ z@XaymDuHjvpH;@a5tLE**}`fWaO)!}Jd;_tw#LltJV)&27Nz-719aF4^46RWf~HD- zoF)7-j(~uQm==a+&79B*urRe#?vHf4>k+@XqpKP3PeiN>J!DJHlJ1x|?w@&U_NeDf zQ)rV|LFz14-^ObOh=NkU2al|)++)l$ zjt9yt>(lH<5zW_WhCK7hJMlBANp?!5<%ZY3CZQovZ)SLF92+HUkVMg!n~M0-(~->* zxXSWET28;L9EGa+wvbZYLwq=tNj0ded6QYo@t2+6ADyx_srX12nZ#IGHjGgsQgE9h z)o@Sx6K-c$m03t_HpHypo}u`U;+x|F)Ah@{7rB&bL{|LecUfA#0~#kNa)iuqoYmqsP7N)!tW0x?fpSDxb6##`txiyV?7X(()!ofZSTSTl zw6;cd{j0bHn8v8^kZCy1>^T6Qj_QScCyVB=X%=(7oEZcKDnkyZbF&rC)!?6;371xR zb0CPi2D2f~32@^n5)lFW@l#HdGsB4l9V_|LCrUx|BAfob0~8ndkxflZU4`AN`4guw z5VDP9Xl|v>zhrdR${aKq6QS5cu{~bk5IIsIFv;D8s4mj|8?Dr^st%;^P7B~6bj8$h zo#A!QWvW4x5OvLF9~uhUO{-9IahyRnPYPlnYznjB)?|7aPfR;Pb}eFabinZUel^07 z8x<~UpR%tetiyMR+z6sjCPO^D$ABjMk)IZ z$?6YdglJIMQ+^6C*$!y1$0^P8O3Bhk14=B2ngWh@J%g@F-ehj`+Rb*;myC_4hGDw$ z2zEF()-?GKFM$8H=NC{9@#h*JOC_6_D7%H6EUdX?>z}1cLgoxih$%Ci>W+nmi3?7wRq1g(7>y7}mCiCtV$e z#%`^j`4!|ke=T$7lOE_4ncE$nuKovkNuw_Aq$I=LbW-i$**(-L($_gZ`cN|{x9Avu z@bou=d*&`R8fw+_>~G~&YUG`AJ-!wQ+&FCw1ttoMJGHfvUK~a#1$$s337W>MP za`V~ip^b*`lG~cwQi@9R#impg7cb7J+&gO20zYkJ+_+ znBTb4_;;;bj!|U`(SP9=2GKx$Wa=p@pzm=N>oJ(uwe0@EdxAqHcf>?{t(>6`%V9 z9YmjCBqJ_x>9YIW=n*b-J=<9gs@<{>F-OK)Czi;nTU|v;KNjLbXQ-B)tXc`tE#db# z`v&n5!VjDcgYY3;dS`O+I;pkj&rbryxrcVay|3WqFY2woNTr`6JuAWT$!#E`dL3Td zYhY{HlLDRjV^utJ3M;hHi+fL&I|V8=<(_9DS6Bn_5!8dC^0}7pH`i8j8cFYE1`bnkIoL8F+!5qQ)@< z5J#^PlEmhr>MG>#z8fx%Y{}rvcIGyNKc#Q})qzqjDO)(OY|M%1#$DJdY3cAa^`e}z zw)|rQ%+zHCeACCybAQX^O{nslxoTKkMu}Etid`z&f85O`G4ZruZAxgOxd72r4D%} z)t}aDSaG6FdDr(C@1bm2e_QXJ|0z_B%VaKI8Kcu8XT1GoPtxKPRm~WUUU=5tloG|A zD2jnyQy(4D8)jh45I{H9`ik$tr6~XnRMS%>bKA;zJ3NN0E%W}x%$f#@JuybU~d?9;tfMMH4N4!~&c4jQO#)$JeiWg!mwH;UjZ6J>b$kT>ZwuX+ zjj5Cjk3zYXLYc+fHP0^A1JlWUv6+f%H9UIV-0yR=zQ?I1C4}rCYh}>47x%#IVkfD9 zEIA!FDvU-8K!cr)dR9fnW9`b?MQ*|NIBaS5!XLsGF@%@-bXQ{+vEdO`G$q^@O@mF3 zvR6jVd3;x&I0oH~;7dIDMc7-uCcwCowf9N@Q{R?E?8rMbaV%OwahJJ1~e}1tZMGw?9iM@T^7$rp9V<#>b`g{_X#;WsU zzCsrFL`ctsgt!}fj6+s~K3-i`^q6@Bj+1)Wb?yDE9C$OdP46H#4o`%Vkkf;`)@7k7 zWyqeAosvV%Q=u`c8aS+}ag`%`+UH%Bns4!BrbE1A3a}8&Hj7{kUSEw@FRdFVVJYZ+ z8Ila1F`2s`G#Tje7FN_>UN|lVcf8?egcof4El6GyFh;U8LdtciJf_Ug{PNavE3DbO zSgkbHv`7=uc+166C7;o8^6#}dZizffpi5ulL$gSYyD6Wv2tif`Ni6wd-3$^^41)Rm z^vp|f+}4p6MNb*9nq8IOpairH?_vqaUZOqA{?0$FZ$yfzDW$l-dRV7(BE*N9kt-hd z^_1jKXbelwY)0`H0?+7)a?iA}y1(^y%|yExNv&oOMaAsL!h$5U?|2{r`z75Y@#DwX zC0lRfK@ezfSpW8pQ|XKFL(R_Xw(?nw&KGmZC*ohf6eazMhg#*c2c0i7sSo$RUo<5> z8Ry%@3nV&UZc-mmSP0vt3)=0Ux>7zg%f0dy0+SD4#S5)BQeRn0wY#5$N@rmXA0x@H z(D@T&48ICr=_={nLa5m2YEo0woJtlqsG9qAZu6(*8I~xkw;+ath!I|&xqh(v((en=82n=W!@dlc^b;o?lzHmMBwNP3b7|`D#|wWv zQ?`7Zi3V3eTc3ZfqcjuL(8ewUf*#MHnw>jxs%QTC$?Y8m_7n$heTyc45!$*Y(e3$u z2C)mZbcNY~)voq}f=oKM`H50Q)9wj;QTjR$`2HjiSi6FIJi7>FVqRsmYUpBa<;$p= z{4mI%=vKZyT>#A;ft8NrF}w)c3Hm5Nk}`<*Bt#JTg&W9-9y+jS|81Fa_1Ly(-X6k9 zhe@k?`miQlZ`a?Fcl^?|Z?)uT_F{`R1J51D#R#*O5$10JKfnfK7{4QFI!v#3kM)7fLt-w(=Xjuwi?QEt%HtFrb{-my=N>_QfXE z%Iy*-PHugiTbnl_x^;dOGAB&(ge*V1ARu;Js7f=Uirk46svp;Bsz5y(J3=%!Q->{C zA?VbM9>*=M?bNi>7kw+ z@r~Ck0%fIHE&X45>`iDgvK)mqz%&EtGYH}a;7>P{ulMhIc5Gj(vG|$5-kIzJ7u2eN@JY1pUY!Izp8AQs|ShcP5OD$|TA^H%+OPqBetxp3w*2g#S>of^ClG{OTVRbe$`kL1= zr+=!u>D%*_Vz$LLm`*a>t+2d~^MVxSLSjqIQ+Dy8=@kGWoFPq}47evljj-ttyOAmv zyr3sd(z9VEux(qJM|sCTZnvgBZhg1T{1Td5vjfz&YWuT*2=ilasHI6ym@-eqUDdC$ z>jn(Fg|NN?ZWC~gnQ_Hd z>XkHc&A90$^MsrxVAwJ9`~g1iGj?5Tf#)r;pK`g0;L$~o8;eQPM|U^}T{*Y|AE((( z;68lz&___U3`!JEz2XY+!pKo4W)wR!G)5I8RU+S`It(1*IuS9`im7%A07O}_{?IDJ>+JAnC+TQi+9$LY z1t@tzTIIuI7s((UVQcOq(#};Z);(X(mR~n8l~}{BfFFL{nm5bp(tPW^!6sM!#1NGw zP?Ejj8B5u%AsBItw&Gm*UHMWH2`!tTF7t zH>!D64YRGFPo$#YaOAID};rdLM|wBkG$ohB1_fF#zp0f>>EDqv4(dvlA{n9uG=`1 zzDh{CbH@F#)UchF-edQgvROZU2Fx2qyeUffoX3xj4?2|GnqqP1;OG|$poQivZM zTQi#3li=5$Sv{)zLzOoViv4!e5pcD$*@jxG2sv5OiM8oBwbmQf>{q+53u_26Jwk2Wz{} zyH)_WwS$g=uNz<5q>DZ|f#WXusHXx9W{QV(3nna!JU7c1*_Kdg);}_T= z;_BxFm-D%Q!O#k{y0f6IL*E^e&e%_Eb(_Dqc!ssiif@(_?7QP^Qt2@YRmP{mRxi)X zZXfrp{`8J_cvrD`Tg{rf6xGg#E=Jm~)L63^V=Wo|LPSk@2Gl$ZWVH@s&57Q z{aXjpbpv{C>0=9@>}fx+qfS7Z*JY!9qYUZPZniu)Sqc1|>N9H&NXq+gZ0Aj$@TwSG zt9vv&kSpeRO@1m-H`3;7tOmj&1YFzm!wd($=L4D|Y3r+gb~QY~YR2G_B_gbOt{D@? zad25ud-PpxSi3lO?z10#z7JTspo2FM1ZIJ6+cMo;nugeMWIel9fdQv=T34O8w3#6Y zDR+%7T>}#-c4A{#L*fGTZ#{k-qy6skEdzd}j6KN>J;^26h3)LqYiOa^y8(3hZr*F( zCKaYREv5fPf7y1L)$^U?_5{r>bqr}$2{!x)Pe&xsM>BA^LjQTq0^X$#?Osc@^D)kT zvyQae=juKRGdt0BwJlh#*LY~Zp7=({K0;SMbGF{Pc;)nP zxo-N9^76cK`JE$!Ki&Ykx;aaB_BTnXHM=)18G9```#szLx_+HY@agav4{rzH6lN?r zKGfIA56R=}DW9+bBLb>drLPwUvucOo_U??yFupDk>D(=_q>f1@W|4W#bL8MPIlZ70 zlkglN?8~cpD_HydW@JCF-!GBZ_jyl+CzuCNyayjy(HG9{301w9e>}9?!Fk1C|Mzq; z^A*r}+6%`cZVzIhXdAL`hMeuhKVy`)q&~6@L zcXQ2Pd=3O{Hgx`T4T^eJd@f*(vU2CiVa;4CAG|^nSDM0V|HRvdce7Hq*tRBKZ!z*z zwkCLMUJ6}&1F9W}M+*pP3qRb>a~c_H1B0X9k#V8Q{lS%uNIPWWPHeot352INiv}uT zSPv&L4vx9d$PJ^f^kILnb`jt%{^o(L?W;40-SLlM&}jgMNV8Fky3A|CB(N?m)Ykpp zm8Q%4n1a!l^c6NHyP(j-bkwe%qLQnh`3^6Y%}Iax8xuc5SrE8v>pp%HvWJ#Lr-+== z<{SZL)k&6&y6l@7L@=7B$4P0w|c7Ja(~ zyvw5mrJa{$2osoUa{P9JfrSCIeCd&MPjwi`x}M1F4Jr8m9j6lP zTAuigo>M+#Jv4ZoYsp=6*}uSm083peE=3lYavf#E(@N;)uyiz-pTy()UN`dn+nBf| z5C`k;R1njeH|2umuAt3NnWZy>%oe2BMGT>w7OYgO5s@G@L}VLi(xwvo(iS-Se{#Vz z&4>0yt|%LSYy3N#AlwD5f+?Cz-L-hY^%k^tU2Mp_%f)t`=pghhY&eB@QtIiaHPZUT zv{2N|FR}iq847F1m|0i>VQlF1N@@V-&GSI2OE_;i9@r;yv;DHQ_UCrj+}k8K11R1f zpsLo1pc97R;@I8Zvp*>QM6WmOqHw~k168K+V?!3H&z|-)VS6 zTj;2TQp^aJ`wo{y`e2<);*uO9^6nrk4=Lh=TRh(7Jx{oKsFZ>>YS8ZH<7g+?W>*Us z4#2J{rA30`8L!UF{XorH;y?Wr@MpnAgxRwUvn5x1zQ6I*(n$O%9QcPoSC)~x8xXPM z5!geitsS_sI=aXOxCcbndr5a^}f*}>QIN*5j)LPg}_%^_EKs0%iM zRoCHly2&nBQHhY!hKp(jhWi}}O`>97l!%ixC|)xjtanggNfk&9E}c9pq|3iEmX3Ot zck*7@7ts*G#+kXtl+A?x(9Wsh>=mvuvT#F+$T`G6ei+$ca^Y08_=i>wXP!r2&gO1f(!Jj z9s5d5>q#UCxuECDRHN!*(3ZDzgK|vX_@`P5 zcD#l9Um~#wQfM$t6~|Q~m$c{d&BQ&b9|E+J|M`7#I6@xVFZGgqp@tArZY_O;avihI zKh(-ayI^B5AMO2zudMmmAgugbe0=l&LwxLL=W5|(VP`96XZ(M3=RWZ>asz@$!@VIO zT0%(RqTx5;SeHY%!?03?7PF$4K2%u^#;2K|yWL&pg%J_z-EUz0K@$5-o=o2r%Pjh) zJ=+(&ej5VBQMygCSmk8$-TvY~&*4%Oz!GUBzzP;V7srr6c}yrDnhC`>;_&|dN$~RY z@x$(6Bfz?aoJ;#hB+_Yg-q-ld=0-7~w6>n7)$+)TeBXi(QB*q;SYR!!54XQ636|zv zELpL(Mn*tEy|`V7ENeVk_ppA;JL{BD%}&}&aHBeIVE!3l#>n7|*Y@X^@(yBDH&xyp zu?%a}ieQxCogb+#Wk%heB=rQcZE-}-C8<8^i@SIApv*I}NR3gMPvl#!3wx###pX|K zXE)FvBpL`Fk>Hig8u@TdLnuSUCZKc75p&(3=T{rVPDzg`C%WW_vt0og82?If%Kco5 zqG94qY048Z0hZjYkI?^1n;8PjT&(cDLigX-e=iRw|F1qzB|B?t3tKZ0Lu(UTW5fR= zLJCpQwMP3Eh);jIw$3-uqL+Yo1dh8(b!sTafBtgS)}S4lRVs?eG<@p7=bp57Et85N z$6-wfhBQ(r9HNx822&JWp@w1|HV5`&ZhPOwaOBE(6X$xFb=uhclfBAunt8c$^S%D` zJRWy>{uf$S6i$TR&upDI2T)AqE_4UWDsum>Ub2Z|wN9DacU;F1H-qmeEEj9#eG0DU zi>838Po&HYWh0Ixo3DQ|n*&>uEkAd;NVHnH3O9_2+Oeb1TC(kYoQMkc=TSt89zzn= zEw$<31M_Pz38w3oi=~@@kkN*VCFK4U2^jtv#dhv3DK%-^o3VMOo_2ov2`9DANxVL3HW820yvw?HSa)6uJ}Yd{DVC1=_%s>xA5{^s zy%q&0b-<}Fbzm$UW0NdcdH`Q-3_KD0H~I{$n*yGzmxP$Bb=;VuLm$o2NFuw=!DRKR9O=QYCTabkxe-0#y)Uib=9$vYW{L;+1khg7 zTD^5}8z_HsFY<7h=$*U~7~P~FWCK!9;9LLAxC%37D9S$Iw&PuUR3!Nvap_Hm`8H}e z&JPc6rR5_3k$Ewi;6oQy%_3?YZKoV}cT}8+-{HUl-4P*4B|$)t$iZ)CixV9u!z6vgxltT*&g)b>vf#Pg_=DNpUu*k;+_&Qf?ooOtY{x-iu>mqF{{lX z2k0eT)`ntIr|Yh1Lm}uHFR4HB9jZ0NU%Hds`N^xXd%fGZ5PV+>KB-ty;)YaQ6zNhD zR@o4$vh}VX5LQ<(u&BEmc~WBnHV&0rI~QX7!`YfZwO9&W2!$>(vTUvBmd)KlIjk7w zG;N26reFc;nqs0mVA)lV0hoEf1~_jasJcr-UNU;vlGx|8EVYj!ldqrEInS`$n$i(3 z_HvQoiSgtgWL1;fC5;-_+1KS~_tHx&0vSKAuhSD>DC*|nJ?L+(^!kE__nhshD)(Xk zD_e90!BgI=Q1hbsm*Pa%cGqh~bfi9HO9IiypPxHG_nxTtERIn)$vsz&Sb~s5KCiD- zo=L6@h?6sn{0b*=3Ku(noL)?oUC3Y`I=zeM6A|T1usyGyUQm0(qT46i5x#w@{VtRS zq|hCvquyti-Dh}3@S8{9D5yd!CX_6)YWl8{B-zXdo0|1}O;|fA`(~DQ%QHUWCOaBW@=ix0-Mg z1Ki%-!Qx)+RrGq!hm01Z`N$dK-v0Sh7`rQlmU%ztBFaY{zVY*tNY6;+?xO%fv2*W| z`k)zdpZ3Chvj?fL^?GMC2qm>uha`P2ohFp`|)!od(#G zqDLU$>;`kyS?g<{3?_jNn8{L`F#2?e5aCfiT*TtBH>ycI^^EpQaw7jm2==OB72$b= zB9tFsr(u=Ht=Ks?%QQ9~E!7MZi4T_zc-t=8(HFpDQ&QWIN%VO85|MmffX7-&rBXFH z)L8Z$K_MJRK$j07&kbUjvbO)Cogs0Y8=R^GOQ?#xBjm$@3H_T3VzkD# zN`pvgW7o!k8-pW0xwF-U4OlZ+&jc}vY%h~Osq$^r(!R8Od29Ii5MK*-c|g;{+jGg7 z^RfJi3(!BI#;D_0HeJ>-XylxBv>tj0WgZ-?KQkarDDfc&ZMj@CC?yNtX#@=+zK|NB zYl+`P3A<<)zEr6ab|GJH9p<|rGnr;jiVW+V3ixTN=Qk^1u%&5zYLs1n{^ApYBo82cc_W36>)*(wgc!;g=9Qwi6*-~e6ganljY(RaHbg8{jI zqn;cV@ln2izVa({LYSv?U-nzI3bXZxqWR9hRT@=F*rGzs0q^j%Rg3(XEBX{Gjrd}S z93N2%xhHhfShKVq9Rp1*X~Tj$tD%?%7D4Y_Yh=Bxa-TzEioi9CFQ1WO!Lp=rcus9x zxv*JaU{Rwpc6$$>T_+qiLMGxy*Lu+b-bYVo`G#QYX%7^?7XZI{7{>i**c(eQ*7&Rz zENkc+v2Oe}fYRxdFBTUG8a94r-h)f-8~{bo47fsMS7_%^Z8&g-B00XXl(T!#ENvWU zfROs&9={}=iC+%(^T_%muJ0;6|OV}ieYrpoL%+@%@=<=@pPr69Pf5rA?az; z*gRY+SMpf>5zz~{%YAgOF6dJJ{K8cQL@Aw9oDcp|#&10bZSap&O_h~l<_L}n`Po90 zKJV*?tF*mw^aY#8EaEW@VgIrOzyoS_+Xp%sjt{J%wA&wK0o~NQ#%h$za#FKQD6%~C z!W8c_1>!=y>-}}mdlheXh>`q2Cj}-Ly7HJpY7IV^855qr$`|o7-ER@Dh^9zosp;?< zK@9ETdkzBIhDEg|$@zr&y~Acsb#$zLa9cAnv!D#9lYatNWoAV^6Mvofw?P}XAn3Ow zu^aKRBMF(})<RUjhUfFME5^s@ILm55U`9DIhV5*4F%(W0x#NQ@4C#sRy<->kCz!Sz5 z8?{7Ld#rL#jpN#>r(;p9Z{vmF6g6#RzJ@Yh-~q( ztU3YrDcxu84sWDpLd3S-2PDhK{Pfm|JG!GM5J^!ijW{`R z*XI_udskGR3!@#Qb(r@xJ0TWN&v++M}&C{gvGUj}20_3!+h#1f#sYO`FXyBUAnPMMF?{Zcy0LBVfUil=4ui zuQ30Yu;p}kmAaW^9r_G6&KnNV|5u9O9_TaWV6{`h06K8zAv=SQ z#mv;r@BRH1-vO;IRY<@zu`OE&uF)cU2;D??Y6s`9nZd6z1O5=M!wmWPtH9fXL=DLS zy64SHmP&bL6rzQouSOS~&+ zyRhrU(WK@L=Gsd~dFl-ZOCi-)IOWvyRr1<9N7Zz;Wj$_2>DxLzR15(}+@Gr0JBNbo z9=~!IK~K2{f2-(R$3wzRh$mkzmsqs7$O=cv?t;;@OM)gh$(LgRF8oLhu##K$JqNZc zJg>4^ye(hQMM@9YyBgeyQ>;#kf#9ktBS;N!MP>2|Vwf&)1_~qawB!!TBXqQ64%#F5 zwC8P6UMl7tj5Yf`12h^YZO_XdOwnR{o8hL`l}1u;#C5?&kgA~>ey%g|4U3j}WtOdlzGkaib^9x2;7I-3|vn|O%Xy4aX}`;NZ3ng7GcDN8GV=K*|@H;x_EV}XGTQj|*$ z&`@Hb0|R)$z?*^#r6wlG;l@IZ)7GqMZzl7AJ43LT(n3UN8d{zY63@yDxnz`ac)S-f zGv1r}ZhrG``^#7A_RdDk;f<C&I0f1nm@oVE3U4Q8bS2m4)(K_mI0IY!+mPj(q;8 zZ3TO!kw4Lc;A9wCWe@K$Jqr)8UIx=OsiCyp;3h~HIE>Rnow5qJT>H#~8BFo{QZR#V zJRnR4wSFZ6lMN#?l!(YKxFVPqWHl}L1ZvkqdDZDP6+!6L^*C4}G)LGc;Y#+T|AZI> z_53EDIu{FLr?65Gd`mVlLwRk;osJaU8oxhAX-vHg4+;BoXy2bpD<)RCBsCZ{C~5JT zvePjc)nv399M8o|!rG%{ejOM4X6o$NsS+_Ky44%Dh&tymDm$^P-{H5Do0tVxVO_8o>eF%_Nxt ztaUqeK`w)L8wk(^J9r{25r$%YoM#rP-R9tgQbV(Mx+*yPYnC7V%2^<);2x)Ml;sK) z?+!|s_(f)&NH(or^Q(nMdzb`Fb9w3EevQg%%hn90;yDM7gki1SwY{?#8Qe18kza(5 zUS|aw@W7oVazw*HX6g8&a{ZPedj*`H7OoUVSk+81v(ILBWyj>2_p9Sln+b3q5 z6yBpffyv=>q{pScO(ex%^t-h2kHK^!N}d%lRf0&r)5)aQab$iAQFw z35pQPf|lEfQ>Nx?C#+c2&Lh8O&mM=+afdN=z3=i|BrxQ;3JS97-mZNQ+@?*Sq+IH} zIXt_sZ$7R$PLS|EJ01{zMCPIl$VajkK%A&|ju}w`ZtL$ z(+=Ij6{~r&g+H&34$?Q7R5~rhGWLRn-6TKGJv^jAW*rcLnSm21eoyR!TC4V`X&o`8RMY$E zGKHP*Dd)<0D!0Co3pcfbdvZCvJj8-;>b90gWyiYL<>W3cYb+&QFd61-zFHvY^9+R* z)XAT42=MDG`H5(@z%tNiltydmqjlkJHC?IZ&QaDd*S>PIprz0U#a)q$K7#UCcRIr0 zC-X_OdsqP-ZOH$flPj(*HX}Vp?iQ%JPK;b__m;4ls?`sksI_dDsMLxY3e*NbK3ag_ zr&K#>6D5pDIV3_}Eciut`e=y3JU&h3OVP8<Bj|Q4D%9K+&vkR74bc0*6YVNLT}nQbnh@w?6A#WB=_-r zadJd)HK&s+5C~_kVV#$f#EW8wn!1EFyHZ!R7$v?q{)ZcR195t&GsNn7EOMM8#=;XD^}Qc3jrCY_6H$6eY2#|in)d!FNmBRjF;kJgE5y> zb8QtnqAS%J!p~iAQKn~mK+gz6-i-%9?RCO2H4;(l!yF%>lB<2FA5wgnJ-I(a=x?bg zUtnEtxp;^0Uw~t7N&XD7xkcXGqUU{FIobZ?bb`=!OG3*SsZ+!sfzugKFZq3Pet_XJ zf_OW*pX1#CxoCRVZrF8)|Ch{9up*1#_?5kep#JsT|DP5NB>yghsebt{7(4uT(Je~V z+zELF^-ng9dNcRxckBT1Z)r=(R+~u4KcNy46wRXiYKP>~qpRvzzB8LJGsYH5YP8>+ zNwgD7ug?dO^80^nxRORUYjBlp!M_^2$$oO;u$J{~_i{C1PB!M)!|~i^eBQd*zM-{! zeDuv}^_OtkEh1MrxW=mN=Rxdb8SiQlZgca5ZP_7c@I1PcaqpCTl2JL>lJA7LNg}#Q z$a)sN^&>yGcV*!vKL{~;7+87YL!Lt62Qxe+*f>Y0?i5AZII9G|9$_W z#llOsuZysxi&6(Mm-k428vFIp5*Cr z?WXC;^l6Wv@#W?&=sanaakuq6pd2g z50ADuc&zml=%(%$MtOL@1Cw0axyhs|96h%&r@5R4XJeegSd=kU1@hd-2Od}Qp`?$J z$QyMbs%3*C+$OWAr0BdW=!s0(H6?+OEVk%I`iVS9Wd%$C(r5&Aks4jRyQxH*TfCR4 zD#@g(XS7_A%9zNG116?1EX;$kE-@JDBP_I5qe}XF&ZAV!_gB4p*Oi7jt479(#qij8 zqiP)K8W+*;UaqLFCg8NTTif1Vy>_rggw{W`IQ>q;mnnc%Ts*nwtOf`qq1n3YwMZ5+ zmg7dRzt3Q6Zj2jySXZl=nOqoQ>R0I$T5Z&~oUSQ}aHodf8c;l&1;u5@UqSUlv$P4w zQ!qCsOID?8b^8|JI~d>lQe{nHs83@g2ZJ1OS2jcyxG%6e@c0k}a<%qwUyLqb*OGRDR-#`7VTT6ttO;sD67!-lMsQWQ6rB*<1daf3} zn##SMtIwoU(>F)}`(CR!pnth+CqGT*K|@w;%DBuxgz8$^dJ!n~*qV`3)~fR}dN)3p zEOx#YuEnesz`3L}zE<_x_v6mjxCXUFD`B5wrMT621ma8~^Kl5G&2C;Kmn(Zoz2Oc7 z%Vkd-b|<(oDPV$G>xVDs&fKlGSe8P`O015sxXfu?9f5xd^QIqcVV#sQ#UzYN!{Nj< zJeNqh*mwUM^A}#9+`W)|!#C0}6K=_VEsK4FE9JVYba33?0>5HbBew=4B~{seMY^Ml zQ_%1IYBr`$;#t%3aa*m2!l2V4Yjm$-&%SK9ziZ^}r(Z2bN3~atF?~pr+C;d*HK)57 ziv>E~&cA508tlfm9EG8iwR8iYv2c^=f8}-&x%Cf5ze9%Q6HW;>WUt;c?KjBb_Mx5r z4g4GWLH8&>?>&JS`o;y9l`)v8lTCK1?-2NQz8XiVk4y#dx%wV+#@b{)9C$phQw=M< zet(~66+&$3R>qb5oSz9%bR}W~tX|K6_Mz7lA1nZp&I{MVCB1Sw-RRe8-7pAq5k|AL zk;H0(rcgJSaP>PR66hp}TRhR;i!jhxiWgfeJwY)r*9%6Mr7Ta!6eNdbXlsVO!7Ju8 zv$DIoTdkTSWXpqa<5+IlKo^cXlQMXS@>O_3$om^`N?Ict-=#R zAEGN*XW$_ShW}XSLKKFNL>1(N9#%Z!n3~x6D%vU&$5cQZ;^e_ut4a{el%JUaJPA^p ze~6=+vNmpp(*nrD%>mY;7PiHq$bEh_aDzR(*hY5&E>QcmUFe>X@_l^-0ZY(LOEZzE z7Gj}xWeMfE&|8}EYf*+p%>bH}^VR;3`_{SyQ*aZ8LC-Tzl*^GI4M4t zw|3Sw79<&-=ujHJa#Dx8EMnE(&*hDPZzNkRpEF4~-0DtpU=XhQExxrY&$+Y=;pgv& zoGD__`6gPDk<>u&-~A!_@aZEb288iYM~vZ#hE+&XT58@2+fX(IAIUwS=hUu*&5}%8 z(_LV#an5NAx@`dqhD;)e2km}>m2olhlbwO2!wP{F5`m`D6gXHVG<_I9`SJ`xch_53Yj334PdA^p2?Jz<+v|@XgP2UY=&WK4m37f&%{}*?hoOy>R(%e}YWh z(T#s*H2xl8@dk@Z7ZhjEj(0MQjl|ws?ww&8?@kNS`LSiG{?!c>xMwPr8l&Ohs&h!B z)N}w5lP>peP9-5}s_BQUD5tROiQv>YY#3cJerwl=Uy;Msl-QRsOg^o8P{>ySM>DY$ zZ!hx*Ty!#{48|-6`qInxEmRP#B)@I=6OeZF-Xm{>Too7+E>WjV#y0;VDViOQZT)G! zDVh8T)D&nZIaj(4nMCey4yTvCMN8qmYd34?Bfd`J`!)zeVdl{vy-BbjPt6sS(K^>w zm1Vx>0oCwG1XR3sAQ3>8YN+Xt^feNvXzbbL;>Pus}Uu znle+dq?fH40$vIlvcOF)(|oZs;9<4zR<`b!amK!u%Lvf2AYsGA$vcavgH)fUWQfUH z>aV>p(%m)6*||iC2A`_lnkfpG6Tea63P&m(W? zgcYxg)J?h}TM$U^i5kJdoyyuVi zEQ?KEN)Ovom#{b%+&((ozH5I{&&B6o{LNUuHplvly9uNGD}RG&_u>AcZ2x@ztwFP) znZCJ=qrNL0$lny8zyADH%Kq*7|5t!$zkauMFs0MC(>F9Trn9m&WwxOE=bQer19z~s zlGC>_wjyTuFA+%l%M=oYN6>O*IZ1#9A(5`hphHe2kqtJCHJv}4wQSG39e2aLnQQ8L zwtt2(FZD&w>q!*z29PTh=Y(%^la=XtyyZP*c1gMp(Sx_Ja2YJN6zt-hM8ZF*ETbA!A*e)_889S+x7MZf)D=WrD zE3fXx@lZZUh$!4JK9bU=H^zLPR^l)gS_bNUMG5gX{x40!W@kW9JG7hIsG<3N#8l&1 z1Q6g_2PLc(?VgxH#SK}4)>^T|9nY*RKRA@Htd(vZP+?3pcS3X(kr&$dxh}AszIr}s zhs{L2649N3?d}wTEr8NM0)~DSCq1Lv$ zLSC=;EE%*5f5f!$7Ci<`;}>TN1H@#l){f~OGaJ&(FsTOFf|NBIf`v7QN>$%~1OJ8V zE(ZJ(#xFR!{zqlye}P8EzoRip(OPyv4#np>m8xQ>4+m7&-QGY|CpBxN@ zH1P2A)|zNAWO%&{@;-)IGrh51#(=e>A91$A&WQA@N#r)IodaFPb^)bz+YVi%&jW!s z9hxB>ooJA_uYm*|ju_*`vE3uFiWv2nFgiPr!g8jm)s1c`_^o5z_^g|%9sB$i@FXTi z3$o!A=;&0Ud65OXu<*`xlZt$gN8ke;c?rQ+hurx^l!EP21dBRD@Iy~J9i#?Xqp>wY zc<|-iPf5J=#ma^Ny;_~%ywC|CS)?Cmref1WEEY?I9ic}XGFg(qH0!>IDB6e;&+3O) zP2z6V%u~z*O_TJ`J9dm?~p0XrU69F z>3x%Ha^mP~J<|@0*Ft%C(Q^n2X&BHhWeyrvqQi-T3F;U&=4K>F0$9{u-%&%v-*tNx zP#Yc(qUI%x&RQlbD9V2Q^tiXsGG)d(z^9SE0bRqWPH6N?Y(~kn)tfeer!XxGK{Ra( zc~q@0k;`Qgoa-eZJ!@>#>P4~Pr#Y0~#M0}^Jw@?$oJJ(TA@Gi*8X3*(PhJ0=UFtn6 zr}+C8C^6pV0M3tLvy1vv6!^Cj-09>ur>FYd9H_^MASuPv1&)W*W%Wjg^_ZQ%AnTMd zldk#&Sz54vO~wDyyX$`eS*HJj?3N6&Ji;Ft%?>AR>Z+h!I)BuSpnCF6^VTv0W-#gS zF21eoM$MLvscZAR7(HezZ)D`&K^Xkr5XR{>v@!<%HKUp7$+TCKi6!A&K?o0fcJPwX^t_g)9!hmYkA8 zQzliHk56&!*=V=JwiVr2g4dZ_)P|umgWYVNb(*qK%}^|HEQmncp1W@2Vo?|YjM0dy zduj^{9;YZK5^4zpBs4N5o6Ns{Vn8l-D6{tX1H8#1RvYx*x}}fA?-=G2h5W5QVVP;? z+F8u4y0LVgIL0V=2xf~y_&cYMZf0R>u|a3CS>~z7(hb;`&4!X<+8yu;O0yq80(?G| zA@`u%&s=Mv)}*(;Jy;YR&oCgE%X|XB1ij=?27ck(MPqetaCS*bDMJocH-SJ`I*mT8 zO%i3xK3KnV_NF5gwv6%xSBD^jBW87Fa1=o=YT0H=ArW$;G71npRq$& z*K<3p!WFUkz6=##-cmrnj8OFru@GNXC1*1Tum`@pauFh^IUcxD+Z+N@+dKkW)j0+B zvDXYo=hBF6cX>?Cml^n3i+W_T-}5)@z;+Q(3hj^hrRE4YAHS|5S5 zusP-RSD3fnB%lxONCEpTl$%5MO}mx)6dm}t2d!CTaIOIw1HSMasnGKa$X|%futB(2 z`9dtw{}p1H{{^u>Q2rEGX}8k?Hz9lo*M1%OXh~eKY{HSnxlpU``dsEMHl}0Oa=Y@8 zA_ca3z^QI~ATM&mOoDM(Ac@j0Pnk?7UMF09?-$Q8IbsF=B-nzfV}1SJ-YCEBdWWg; z*2(j%`kLrX^|Sk~?`ua_@`E8UC7Q@>JiIdAY4(ET9m}p>Y4An^Z#*pDGrXu!h94*d zZ47nGljZzocFh25x2hnBtQe%0*4sy(I`!D2#m^RsmlTeI^6E>_@%#I)Y2|TB_9)$Z z^G4&mFh;q_nA@;8%x0LTh%_shjLHuB7-e7zl{Ze!LRP?-_f4ZG`dK4>%bvrLw2?rI zktqt{tyuTOh(`7hhgw?s3b^$VYF%44cEY(s^L(Jfs$xe~LVsjJ0#7uUq#8Cy0SoBu zis5Qb`PJ;=3{pl$ip(-joE=9x@I0SMxMs@5R-w{gNtDha=D-I!3X% z`}uT#b2_ij)rEY#Xv?L*@{dj%E)2^jq5lV3#~X{g6~54l_kV>}mj6QQKPUmKYUvQe zKPUl)@Q^H4w-iIV^xr8#yTMBQlTEKIBNi+;{apZGFaFn_X<&bbUbFd)FFJ6-G2?z# zv%}{@7(tmV4bXSN0KYCPsEmFmJ-+P?j0y)D>Fx9b18o=lD5ovQ?15RkgB(==umSfv-rh@Q# zec@b3^9xq^$e2uQLh{74d?-$8{}wmKsraMeJM&{>YiBcEsmiC+VC68fig;1A=*7Ns*yeTp_Y$8k%kd^m{-r*aXkOT z(-&BEh338SJiBP2=n{+5j@yzkO=z7;P{K}!z3np%0!M|q&G0XdH8v%=>#$1m!%LuO zDXn0Hm1t1tE&2wPVTf8y^yd5{NI>9excd#Uu5(G4A}N*Fh~cg`mV<9g!`B)jN+HTn z7PW&bt0;3guPDvELIf*^wx-zUX&Af+lEU(gRL{xAmW>1$7Gv7_RDx2A1z3gB9dw;V zDUlAIK0}j8i_c84^QAG0b?v2^#I3;=nTp?>B?@@|EIB-)jSbsn6`~q779Dy)I37R8 z*`qzhIhVhWUd8hSAZ5AVgYz0>%1BO-e4%v_?+dL&l_`f=dA5CPM1eeqaCS%EUXd{r zb$tmfa#$7z^WG8vg%A9dQh`nvjeod8J#Mt+Vq*D+r}Y&^=L^sN1FZ}p`Xqm$mGJ)x zt*n1XYm|bvpIDDNs9e@A*cjN9|a~AF=3$aY-JJdhw`+w8~R44Os~-UTV-IUX!z!ndD4pB4~ha z%n8mm-{$U`q%x{EsVP$fp zp4a4b)GoZ<*YpG9>QYz>{P@a2ZWL8pMZUh?Gv|cV-^1+S(&V|IItJ{qw9~fM@lv@&X_d? zoh-oL^5*|m#c%(8V{{QxQTLI-R-r#*A>#;(o*Dk;uyY6V*U4LDfgIWYHCf^G zANMN#FSsJxKhUA5X^ZsN$t#Dp+=L$JVQH;g>7?1NT(M-9u9P1ZOc_@UKm27U*B@`Z zatSy42gTQKhExF={{x5@JIZ(1&4m9b&wSNrCe!g)tJ^Wnwx`ES7?^~c-X45~iC$jr zFqm+H)F!`lR07KdtAcb{jaWwbapdxE~eV6=AbcIY5FQNK5~=C5Lh(|!g9Yx==2eN&9I=0d2-{R?GtZEo>md`1ASh1$q|x?f#NJ+-gWZv*X` z{1@r9La{30b>o<4{Ww=0z1>Vd+VukQ2u&QCu;C*I%e!$GiB^A?9pPp8*KL+4BQX@^ zw26Z$#j6w@!Qe)8%S;H)LCavtf`aX`5ypI%Pi<8qGzVE6r0kE{BP=6n5hLlSVWh=_ z{MUKOiE=bVKGwZ2=J^M*vEhOAeEX<-g~2rg5;#s7v1GnAT-!>KKM7Bh{4Pn|4|iF?I&4UYxk z)7vFOyOSKr!5+jIR^2509oeYbw8NrRyg(dPc5?^u7hH)WUTGD-l9|PF$xO4#TE1>sN8-+m26PNOe^+^C8OFLwuVQ(90&jJB8L#<VrcMneM?cSiWjBuU$c0}(!(`vVic zX2&v3AupPr=B-HyHI5G0WIMZu8JS*^gDoVPK{s{|9f4f60W;lH1|@+Gn^XALd+FU| z`|m!R6nWtr9q4=rb&~x!mvH&`ow zNixdCYzc~p*Vfy4G<1ZeAvupQT~4tK35nbuRKm(H-Yle?OwmGCJRq)7il|OP+5##V zD(~01NE2rs>A?~#jyNUoUU%VAp1Mb3UyGXT`7%=QT2VoiiIYmpV^i4`RMk+ZdDwbA z3m_Xjoe?GYv2b~PYppu1`J15qXlQvDOL^IYY06n0=OfBM7`0JNdh)5FwI?fyG6&glZB4O!SyDivZTTRUYUuAL^Js(lE(4e2{FNddXf4oYO~b5yk;`dQQ)>Crb)EdoQ0aSh%4jrD)v%4 z^;R?0WA;FUS)$6lo;~v(sih8utN?|TQ-*w%_z4Fq0arjdyVl?WqYr3;{@d6kqZAkI zyv3@3WRr7rtT|S3DE`D(hin}3fJ4S(tkaS~nMr%_fl;TI0FiFo4T?`cHJ%FuQ-<%y zb=XT;WXc^AX3cJGBvEGP_}C1`D;S)jkw^)#F>z4u{N$NS`QoS>Y|VrlNRJV@-A?0L zo1$ap-XpRBsJ6((Jh%s?Q;tTXl0=kc;f3@^c;{GF663aj)^H8SD=b;h(V=VR2cA+$ zg5xn4{LTjBpPzi;FA0%+2c?m&x9H`+?wUf7BuL~WXk|%R7PcvAhMfN>5AWPoNA^6h zOOI*e5&j`K;Q4U;7(TXJ`b4_3#r*7hKNy+9?K$ndiA|M{|ahG--cN>YR+9b8(AwkLY!;6;Oq`A zqQ;4WrnIcOidM+*;CzWnRWYry>dTC8;C`8Oob_gY_$}hJ`^Z{Tdu3M4kG6~0GDM}h zW+gwlD_Y>5tditZK7yvDaT-xPj>O#`bh^8G)?Ju#OH(Lk!Rd+z2W)0^bv*#oBJwQR zvx>4Sm1!mBU5916V-e#S#l{NwN=!R3Cyju|*xUtaYH}T)6D?llmmSo}aI1`L?1rgH zMRO?)*Axd;S%6Z!WR#g}$#3Q+C%2&0*h$OvYgqi+hY>oJ0tug0h0;DbBSzT~L&vAv zdZ&ISg~?@ajb<%vu9|?;I!&ydm*m1_bK=*cEyeX-L=r>KL0J$}D6X3Fq$7(1U&dY9!Kba3v;fFLX5q zK8Wa#Phy~Dryx63zS}~o@_}M!`14OP))EmVuWHPLJi-MKf^yW21l0z zX0_oR0h?Fb-3NxFrAL!dAjbZRn?h#Z%Y9-9vZx>!${*M*p$Govi>0Wj;Wz<3rL zie%!Q+~+dIiz4CZCH6W|m7~I4AZq!ezh6ebP1nd7i3V`bGZLJ$6Y7r9g*yoSK{y}t zz=CC^-RV0z`Ldj91un`hjW~b~o*h-yjulCWqPB|#!)e=~O=EE~?MYRf^n-AWi*Zkl zd`%4pOAQld$7U(*b!SJHtVK96WH_IV^q_16rz{ci0!XXLW>VDwy43t=TEDHx|F9{D z?@S7}he+C|r5t8PJpiKYCxdhjz3lRDa>GSmNEt7Bly|_4jxTn#2g~?@PmX%32b0_I zP@B!72cqc*+JTT|MH5dQ9YyVj*Brx=#ij}yFfd(Y!vXjNIQfX=$JkCJ&w-rIKu>cu$|>;m2=%J)dC*UUIxD2N^S4EOIy7NQA3Eo1K!X6VJxw)+m@{CMd>zgvMRxvjuG zVXs2_Cn!fVFqmIffuER{3E<+aO)_SIaDAF%XbNy=5%omX5m{%Y2yb*m{7rt`?}0lJ zLZzvAa{$th7C24wB22&*$mL2kUGVtw^(L#M+cU;g|TqVPvSTOJe*pc$?reChb2`gip|sADdFJjlpB zo8t|J^R)gnC7Gy~mr3R2Qsj&7Fst+v<33>;hi_+W5PX&17LqK?1VW2h)j2rJ8&(9mL9p2yjhO)Uxrr~Xz)q`BjT19&nxE=OUGWSiK(<9DH zW+PlIosy!ED`#$O+sOT%&RQ|pPMz><7OmoYBJtt|esE|Jp+h5$q(%$6Dfzog(c&!MisModzoEKu2a+AWi{r}fDLTA3 zLFPd1H4u2w2F^?r5h)C#3%ul`yfjAB?M=krqr&RK?pbo~x2g=Ruy_d$uz^u>vF6a# z<_@?hFgT7%0W|6&vozXxmi0>%k{eWV+r1x>r=59tu$#rYua4oEjy1Tp)Jl?OGAJsx zThZQn6k;kiBw3xES(1mXzk3{F&l<l zjnUYnzxZPYX+Cmhn77Rh}mXxj{GK56OmJ}i;%VoI9z8P7NCHw zrBCKr}rZoG9s;h;o`b!d@lH_XcoK6h>5XUXj&|Ca0AB(~@F^$z^32y$)Jke%l%>WjvF`K@8*j}wQLsX$&4B2Yinbe<#LFw=v z>=?hnVf(iCr$l;gl5DDAqm1C;>Qw^%*eHsMRi#h8HRq zT6A{neSws#=8b{2!{96^OR;oids;(Q936KUK0HH4%}gV8E#cS zrdB-8E17Ryxn(Ejbpj666r`9yK4Vp0$aH3J=|Db*eMgxY96>Em%vYsN4KAct=5KXD zJ_kNYCedL#2NTf+(ieYhBcNu6NpOG?Us>;-)d+`>mIDw`(g_6e(~a_ACbPKPVHpH5 zB9*rg$o4R$7cmS-b?AkvQio2hM@J_C%!Nu4Y+B4#6XMDNX}B6xuEDUKaGm1Gj69m1 zk5=+$YYI{`$@V}ddMN0Y2F(P=p`0Mtbz`m*#Hz6f6PoWE9`ov!(w3Dy*5a1_&DI@8 z>Wd^4=}qxxdzZ#l48qo`qrCxb%tG4 z;%b_9_7;w}>WrzWV)VsR8Rf=PRiyRquX5cqLU0@^NkcN-+`87;ZC*B%BQT@f$j z)fry~nTjbE@4qxKYEve%L~tPMUYGVsYO3+e0!xxgYEJ5lZk^6g!i43?_AzL^8h91u zh}NtEBxnKWul{pAfn9iye7#$v43p`&kEo3!wO)xKf+)<`AP6O_L(} z(V!bm6MNIy$6P|aAnk#80eWFet0=e^r>enfs-yKmP{pualZWc%b(yWMQ7LCXXSls1*hGGG{@giKwn<2W*RSe%G*Re1jolcPtc zFs3zyNp4&5Cy$|UH30?BM}b>gU{Ht;arJlQ$eA$-@v2Gi1p*tcoF|W@Z?G#=5mE<* zGh=30h@7_JJGMG% zXeY*EUDtLP3C~U;?o~y(jkcF*n-YL9*S5XNZd`X!MfZ1AD+G zj@B|cA6SCgZZ<#<=`g7~&L#^5YI1%duy~U~p*%f^%zr3UHL?-;p zfeixpHC@((GJYv!R4qDUTai=)_G^EYOVr&Io#ca6ZtE)SGYde_?)9b2-=)YGQPE>} z*g;npKDf${RY*=(7kH?HWriT754oapIMhO!vljS3qRZO^U8Z=O&YYwuSv=RhK(l+& zF-|?6Q>j9n1H+Ri8KZ%s(C%vS!g{gZn;$Jy0w62HgJv$xrjFSer9acWM~*=9-D^1#^)d2RX(HplIHve=&xK5+quTuIo9tr@hf zA#Ybf5uWCC8Rl8Udri#L`iXlY3sK&?dUBFY( zsV`9RzTeZYN9&oqYzn$L?*KJk3C1Q0s=0nU!4>JG>+}I&!{rJGx5=HHeZ8M3KPZtZ z5zMs<@R#lg5nw~G_r)Cf|J&`h|2duTpLIuJ7h@Zzztcy@zf;F7C22=&VFYi(v3M1o zk!B=plo9dF*zZ*bkOD7aDE%Q;$k;?%1GqtJa_?@<%LM zlA+(fisE}ydzz3b)h$hnKQ_wO5@~}HK8W0ishlYZH|3Ou?%s=|NPj!XCB>mLCo2(m zc_@Cj|1trdllH@Ru$(D%a%=f)F(9{eBk7S&{}QkZr4ze?HFln&$@? zR+C%*DD=1{d!z@4^rPpuX3gt=i`N}w9fg5&Ob5>-$B0LA_@D>3r0^%6_TTWKxYhEn8J2zkVMmVxE%Yo#uC5N z&jP~1D^`s?wi0zX_v4;T#9>ZM1`rP)d5A6I;1K&!75C8`X9XMI!xVp}%owW}aCif2 z7nslBMFpS6fxnLQ$Yr zK^{~d56wy*+l#LkA~AnSK?%d;r8STQR!2h#PqR%=@0oxhj<+{`D<-V zhT}|j$1^dXkI$cQ*3Dkctl&;?#evr7jMgH>m*!~Z>qerW4)M(edwh5&?m;5dG%*8( zlGnOuGPm-*wDrJM3fKjJ_0j7Ipv(AdjnsFfs+`_(zPrIDxyTyE)}KJVoSIrj8Cp1a zu7JK`9Czi!xnb4LpMuJ+P?Ez?CFDVY zf?Kqu>|t~n?Q-iFz1NGdFnEK*f;J5+^QCi7PB4N79h*VnF&&C zb+^2-14{y2XFn;p-j72G@VE;cP%p^)^=TCR(+l^SYyyHAX_m@%i)EXii41GHi!;si zNO1?dP?G@XXfXuOZSm$erVtWd@)VCu=@n3YX?3w>u$1s?l%t)-pX0BoerZ=$S1sge z+!ecenH$74Fg!?2kBvjI5$%K!g%j1~Ak?<&N&LVKpYN>Xf^ucd#*)yp4>7VyyjTie zwcXh~`yHEnd=7y5&+o9aJ%zF?i7H*^GUhVn+kTY(9{ZtX)eGG@E~HbD$BKrYVqS&9 zfD|*Sz&?4c;pfDUi4YZ4bzeP7df-*6*{L#3kY|r@ARZ*VK4ZS?jVJIV$!FS*}R=iC{A+})d4ds z^}~Koem5Ayo{DU^*Ro#O)0b$a^;OZ6dpOQ*q91YIncIF(U}Z-9s9c`{0niN9iR?LS^?p!tut#HDQYjnt&2{x$rfRG~bPM=*U<(R2t{ zQ$rW{Z}U07V{ZTpU|Cp&GLW*5)?;`46j*SfsUnd#UlF6iCvZ)Vf9!@o^`2`q$NoNw z&?1h#UMzj{-hJ~Yl-Ju-g|VK7>&3mXf$Jgb=6TxdAmz{VrDN$2sy`PY0si3(s0Th$ zdh;`9p1)5ADNvtZ^Y#}NoOXTgo_@M8}8qk@W~w5GdvG6 zc7oZW+S)$sUHNo^+@Or#-0y7}c*TT2ztsVic)8_Z?A(k%-2S+GBH(l8y&S;2vtJI0 zdAXJ6;|-3fcnO5hK}qI48YuC8iG@r0BCrh_o6;7lzSI z-Tx5|d1#J54Re70(Hd&1q@^IQB!_&~BlyF6aGGRbxD-(~Z7(uw)0q&bEB!cU*@Q5rMJSnqY9!C}iI8E5neY$oe?;fq?KT?U+IhOE$y(tB3`2J64oJL0#n> zMqLzWQ~SQmdNaxN$=vX4xA3S?iVRm(ZX>RFep(0YU0^9*hq$qOjKGlH z@M#T-siEX76xfzc7j;p1J{gn7C}b)VMGhf!)t<<4aI|R1)qMVIeADZF=y z9KCco)&@1OnvEaqPj*-<2c^`JvQ$~?q#UfdNAG0RYFlNR-0W^X0_$*AEr*D%{)%Ue zTZ;O!T2Li$Q3{_)T|4kL=DU*$wrm_=(6@~$ws{~wnlWs<(D;Fj=ZFTawGl>`tp5s z*af|_UnviHdJ8OZr8GfssYKOsw5BIYUdjV2x0%5MZaUmDNRvTNO~JoUmv5cFWI34D z@;w))INy~H=b%?x3-(YhB`?W=mb?7mo81mdo%vfHFWmv4$I=(SVElbitS5sUe^mc( zZ;;lfo4lAzdHW++KK?ltFPJ{ndd7>)$nAy~3e&OiSU!Hblxm7uh*Gkn_nIevRt}nv zVlCab@a9-PpnFvAuwi>pe}2et2ls&ppt9uR6Wn^DNu_8_wcItm%c56?pu!bHmc<{aoBF= z4B%RChWOPfXcrY}otD)n&V;|&vLf5=YQ;Od!-Il-I%h1rZrmGCEJy`A!o~V56ygzL z;L&df@TO;FUtB1G2+DY}W1oaY-1Lm8tuHc+X6({@b>UdUBu<&jvZO zZ*U&GUk7`_%aD52v!gn60soQEWATFhr+N?nr6gEzmv={k$eMOyy)dGgabn%%3IWzu zNMxvo`2+jYeyRGz%$Tdf$a}?Zd+g4Lny$J+O276(P1txti>HpAQd5Wbvbo!4152C? zhC?vhYz0LnIpO-MCbcS~At{U!{*WAdmRbU~$l`iN?<2 zK3F-S*b-|$A7C=<6r&}nc64_GyG28+A}?^ch3Ut;w$_=aQ`dggo)H}jHysj+(N2y_ zJnmwn(8oA|bJGNx1;*5zc(1M%foY+Xk%l&9DbGV`rDl|N40%3@8-z`<*tq-4*m(&| zhb>gA#j|p9(-uphVRBL@8DnW?pkc#qvM_KsqgmvVW9Sl}x367n>F70)XPeJL17e;V zGJewz6}NL^I{MxYAuFS!H2SO!l||CqSvZY-W!&UiB<;$p@{kax^%RZGVbgm| zc`8zF#SQg3J_|4-;SF6QiI?kK3PkI)fn?}o(1APpf0Vsra3x&VE!rL1R>wvMJGO1x zwvCRhj_ssl+qP{x*|F0%@Asbfo^!tX>bZ4m|Jt>y_K#Vs)|_jMImRHPV94;bJB_Pw zq&ikf+Bs9FONwi@6xX^VzEj{>4S~ znGAHpLUx}SZl>5h?3T*3$QHD@V_8^1^G>O0c)_Nvr0UbdNdefkgGHaS4QLkEyrZ0@ zGZ={=vtb~&1SS1O>29M@KkW5Lzatzw#-L4_>S#fCX@R$@#f5j?6klGanhp1b0Q69# zCc_2lRVNz|4CzQq#@hvAqSe1U$t;*{3Ukm_57m7v#N z!O2FhzRp=ApT9lxM8iuv8SNAkvo@m5_T)k7jD%HFS#QxSj4KMzE8U_8h^o=s*FP~e zWeQm-ry?|B%qmlDNmEUEjQ0W1c&Fu`NFo)i=>+OgadN~oGK|#wm05Rf`&=>=pMLHx zD?EY~?X!o2T?L0TkBFzb*7}Ab7v4L7xT46o(*Z0?&*je_8e|Dg`MD$6dUEYzTW(v> z)wmnv#~T?H8q~L8i&_dQ18m@4%&l?<@ihY965=5e_av|dGj6l zZ2Le-#{1#|j{_SxhZ>?s`(Dys-mR=oW-kD9JM*pH-}g_*{AUmq{J>O;ev^%aej9u@d$tfS&a3@^ zkl!&Hg)Zi)iTxcRp>xCqyYVruv?n9PB?gQkWUc$PAry=U05n7O8b)sljjmV& zm40UUuS-5&U|qa51}GR3x$4q2N+%bj+S{xbVIQ)qt?v4#9~I6>gO5_9qwlh=M!TD( z(RS73%3Y<)#NnUhsv!;zb!qu-v*0U`#qE2`>eL*aYA zewZ1)9PJY8l5ow$vbG>+-Iw+Qoq*l36R}Bro2uuR7A!Tf{!gYa1<3F5autCH+#lCd zKwik;;+sHkk-&5@BSFt4YF2dDOH#AOJ-nHsB4h_)pVJk79?{|EM8#Ef!(2>6E$IBgt1@XE- zs{pzqytLjaBa0S#XgIijCVOx-c(~pLU}8Yd4Xg&HAVH9^ZAg~}N}k36(K2x3+c1vM zcG>KgSR0Lq^bQu3>^F9m_s>`eyb9*1Oy96EYt^6)hw_BCB8cT4h?51*46$qOa$ zbx-(9v$rLz6nwBKK}|qfaOa-0!_@q1aKLsy8Nh6|=t$+hZ-~>Szv@F=O&r8j+zXe-m`~Ubm;A=xS_$nhZ60h; zF?5zt$N}w9e813IGru)9cRz`tHt7__KpPlhY;ADIn0mH)=m{Zt7^C1t;4=yvrPBOZ zibcW|%_+GA$Jhg!Njh@D?=JFDt^vw|H;>pIT!3#RW4g5lP=_iqT7-01 z%J%JPg<1)lHLJguFrWLL-Y)ris<}ybSDfy}rf+@#yA`e{$Zc>02Oo`1nVqiPy5Ba_ zGCuCEzu-|^*WF`;ivbD7OvU)V-@h!tOH?v@BdOQoRc8b14c#Sr$ElS&h^?o=9drlF zX981^>xo>MU|J%OrXb?con!}TrO};Q2)mBZ(a_YXoBA;PMHsb}cfb@-u&Tb%Lo!y+ zc}om}V4!ylzYDBj7AvC{vvmY6!d^X%LEQg~;1 z(5JLYi9&yQ6(gAsfJTeYh~u*mQ>Ln3p|Q1aFtqTU7#zCLq{Lw(Ns3v{I-0dw;Rw4_ zTcln2hV;H2aW46#_wq?j!{H_r4^oT;V_%8GhRDyc-oIOHG7dhiY)3GLG?LPjoZ8c2 z@Wjol>pw3CRt}h6!-UZcnJ=PPTZ*+q&13GHXBL}(PRuNKFdDRrUDhR81U=HhRgxz6@1VJuhtYZ#d{|}HwVl@d%+f9Qo?L6w&EkZSX!5SCdr8Qu|-QJ9sHNp*;i z48ek65uUByae=qnff9P|KG0w3GGsE4D(L4v$L5H+q;yy6*J`OSz)~Bik?UV~4~1F2 zO^1qf1Hc(#_Ip;(>?V|R(U*wcWQROCB;~zD*|rJuWj1Ej5U^Ue?S|+V?1E}90*QDH zPb{mZ^Bh@e=v9^(*L4ojr#M}SDt3*$^LLcsb9SA;^~|3YYd&F8usJgjfAj-=FliO7 zM2H)!2Z7NccD1nvMv;l*GYlrM8LH};arqNY!B!S=2Q`r*n$cFz2-{X+Bm%m^6j!t1 zQk*xOS?;L#@SzUEy@)uz19I8>ABO@T`KK)M8AUMCC*Znr#q{w$qgMEqG41=#6Azi} zF0-GiDW*>4u#!A^1p}M?oD5J3@3ZxFb|NNkXRVQUrB}C^m$V7t7ABYEGoxu7igLf2 z%$}t=DAp3uQrwp%DlIv_cd8$#in1;N+Qs%~amr{KKtw`Q(Xa}F1Jk1VgN-+%zvH@& zcqx^;gt&k?Y|yu6&j>6({$j#==Cj$aq7BB=!kOn%X`)qNm80}HH$U2AXByCiO1D;U zLVP6fiH(qKUC2?hIB(qba+3}^h3ta#&(Q@h6`M_zW1;0xYR~1MtOCIbA^Zfn#~#{m zcmv}}J6INVva5*DpU@K!v)*A2BSbDduix&GM$*NSz*4{_d0WsV{D zwZa-+=6@1ZoeU?!7 zi&o>W5;pFj*M#NoSHMdwCyL+@tydc*{EAF%gjzq~B~ln#wlIZP_Zfeb7@MfaJQlcU zOq{rQbqqd7AZ|IrmLK7PC!$EEY1S$h&a*Qd{;`5|)*dJ}*IcuY1Jd9K8LtK0G|xvN z%8|gn1S2TWC|ay^_Wu!lt@kgC?W`Dv#ODiN6M_Aw@$-L--uQpp z1NB_EG!yd{kL+Byd>fLS*4?)1bl7h~su4F&9#+<1x0nOEDDTfS2ZYM@D#lTvWyM>7wT#E2dPDSn-8vvAn0W zy46$cpfYKGLwKg7O`=@(j8m%{CR7fpYPAtcejdlT(u*!fVxHA=-;I&5`A}(|ZvQ;i z`AXPqANjO%w$_!qN&l31re+!{wasz$gJ0HGYhBg?bpiB4XRm!X^T{KW)-v2#T~PG` z67L)39Z6!&$gIkK8{uDE%=C8(aQo`|i>nDQsAi1kNc;It9J6-s$B{?Gna3$MTJM`o zk(lpJ-y*f}jB>=xYtrYndoR=a+S3F-XsGY;7sp>m_LZ)5J)_$(JJLeNuYQU~ZQTK$ zY7ff8vfdsr|8)YEBM338zT`o8|0oY4{m0(^uM_aU8oIiNHrg`&-%ovm5Ql{tQDV!< zKc!(kmWEM-B!^L0{3NBlc&6$QSoIe(n*uDI*Q}lM?G~P7m7R0xEA$Nw68}O~+duBR zuHW9aF7`lZYTp^T?!3C6J~q3byu5Guu04>7%;N-L?RvG~J0^R4Ft%dc)Jbyy4&A{- znE;?aIO;K6$NaBgr-)~IPz}^X42=B>Vp(kW=3vQ=;hu)@Cr$O@lgJxhSj4?Nn2m_1 zjA#E~#62N$Uh6#)4Bm)X?B|wnxzy*va5J>Jm1&yHM<#av|zjYuj$^u`>_BiK>eC#emRkU=KKD&+_O7-x|V$MOMNa0 z_oMps!+nkorhi{X3Byt}l%&!iHI&4DDcV5mN1E?55R?d}!q78`r)r{#l+iF8Q5K5-fnVOYzETbAnuN_MmHx4FR1Zl7AEO@b3+r9~eM0ilpF41==&lv-grDoqF z9o|&Ty*2GcY4m;H$peLqePVHIhRxTZ$V`;wJ}nH{?-W>*g1<)j?TX)`D!+MH61qQ? znuz7FFX89@7Ve(H$9+*es*otonI;X7Z7CRnGb#V|YlresTIuOgb#C3aPL<1@rHHNh zGW`MgjxV#T=9JrCoxj!FU&09I&Jf#v73t`XU1I;hx1aZ!;-6E|e1)d)7?AKf;}WBl z$#w+GIJQbt`*p?{$mZ+@pa7YcQ)tK{&0Cj=c7wPo-C?TSEOR&gWjg$Hj5$IX6pMbc zwQqH~zLAIE#|>D?mVFz89fwt4lOx-x&CC@4xo3jb7p$W6No2N4Nhb?qiD}4F34f)U zxnUTkIE_I;SDIeUcPiOsh)kQ&U6r|$ef#T9mQo^$jyBr@t?=VOfTCzH1w8MGAq&1r z<)AJ}B2L4p94>uuo~yoa0?Qe6SjzN>=ny<9dqln--HC80K{rlFJ<}oZrdG(d??JZB zc{n-JHGiyeZUG6Pt%YTr<$&+&xd07Qppl@w16#Zb{Mk5e<;cs(&9~)|i}YvX6k#79 z`)>YwH?zQNoD?&SGCq>oo3E!k?PaXV?QV1|q%MMpA&iH+9QcS~3pCM9&|#+~}}BCKRZtU|RN<|Kxd)JH2w4!F3u z==TsptZQ`=mNrp>9wqDJ#w}FEfoGJCcnTh630PM_dnA%H2mM z?98dTxvX0?U2hv&_!*SAV+&K(8ck&wh0?88$=cypjmC9b$szIF$bY$K3{Pc_4t^*! zu}Hz51N;6opAw`kYinxU53c8F+8Eh1D?%on)&|RE<9NbJS5GOA$ux(Q6%ek$pVk~| zu9`_vFg0DIoLhGwxz(*6J+b|nG7(Rq+nh5R`L2a9_V8=`M{(>C3xhQ)-M)~J9FgbVenFL^Bz?y@!le<_0)u+OTgVx&X{AlmwlB!b1n({Jsh;{Zr z=Al+B)=PjNyydMl;^HXM_Hs;&4QtMBoNRMW& z@3b7DYr;#>ve_uwXfoYBXxKHPJxGUVPsAizK6i*EilGBdNY(1Vw=h$NPjHICl*PY@ zcuui3_`rf@2OYb4YJd@l_wW;!JMpPHoRD98ZGTgh}J0s|dg za(g9euBAh~0?w?f`I}Wd&G<79$!4bQ(2skIM`s4W0qhwJzPJ7k7>&7_TD-kQ$EwYX ze^syjYsEy=Gis}TlLtaDv@2%m%2{m83)P=iD*=aAC^u>Y0On&0XN;he2L`a^!s^Q6 z@WXN+M1Zq9cuTi$LWu(X0eZ1|%uKJ;DuO~l5;0}=MftHNl0{mh*435%B!HKfLXX~n z>8VXZt$ew%+9Y1{GH^UXaxRZf*yd*b0tsIl8-LAwW%;#(@Dw4$>Q^~Ed%%$b1dCaR z{d!ev=uQ;Mh0_8jVpnk!mLh^s>}X?FmWL8zu}-##g`` zvZ0U7aj+0zyX+jZW3g1La8Hx7{;f0x=boYu1b!QndS(?5hgwjgxhygh7w?8C+cq+O z&1g*te3mn77WFLO;IIQ2WZJ|xkz`mLs+wKm!gTyVWOAm|t~6L{9(k$!MB#HwNue8+ zYu}g4&I}ZSDd0T-lXZ*<`?VKVdR~iRAJ2H1xsTmMv78rSlGdZ()}cb10c+JnsdUnv zB{HG8GLv~USUkc_+ddG-#+~h|9!?uCd^MlNQE;Tg=#r+llfx&hKXP|nzlWrY75Oei z{$xi|F<6NOg48PsXB325I$VvC$q%pT)@J@f8fB6Ckyv_?9x}Ksvgz`mu`;i|@k56xyol!kUPIf_87Jw(MT;ESCPLas z!|#4hg4T}Os+4{_|IFM)!-X_*d`jO+>EL@sm}>cSlFO{h>|&9U`v7(Y)M?7`No`zJ zz!G(?c%UW`(&`ej1{8&T{^=)!(lVQyw3X=_dhF!!xqN%A9qO+_Muy#-F8fMiEVpnc6JEh zb}sE12Zi0^HgfcFyu%ItLc6^a6ey0(RwpPuT7oG?t``x+#@o1OUi9pTLvqLBTrsnt~7}=ZiKnR!3#nLfzyFw(`W`jUWiQSMZw36{47C(M-*%oNc0AON+mpMc}P#VU6^0d1;(_ zU;DJDldc~@tI7~n`;32L$s9S-W!fdrv@~znzIZb#~$TM~`9+=n$KEHg4?I^p9F&oSP{0 zmzU(gGhN?hp(os&3uA`*t0ZNO*^EtVQ6ZVQlfvouEapaEInn+3J@_J&=MYTNsZCiq z9p>&1#UXqSLmjF)fPq~L3e?+gp+DeW@_wso&(9Nc8a)hWZrp3}e^@zDrjxaeaCo(wp_ooc|8QOyWJDA!mX}=QfKDKI2B(!CQeRv|BF+r{0dM&Rr?B1Fis3MMu!hb$Dpe$ zC8{MSd<=}$2d|h#isvs?D9@M-rqyxVH#OQVn?Ihq=iz-;=X}W9m&VRRv?=L&s6H|J z8!_j^#-D4FI%8q#c-48F9lQO~LhZeBB3oI{(#H|%5YR%I!1d%)H;)?@rD7A%cOHH z#W>8A){81nRk?BkHKdWHA#-NzkFDw=1Im24ljZErylOZu3EX9?$~={m*s{2vGMfv< zAd=lGzhAaMdM=%im?)b^(__nsjha8v?^>3{k@6<`sdZ@yf6{K|rlK3m#X9WY4Hk}G zee&fhvTA{JoY(6B0SEWXbO{#fZ;X!f85*iNf1rf`DJT~6yG=Uf>>VtP`8N>-TI6NV z&q}tOp=MT+VbYFbi_vFcBQ-98Ia! z8W2Axnll$v_kY;WUu#IfF!dl=>BP?*SEN_ooVQVqjdm`l2qVF43C0p0qh$&Ti^}%m z9br%5WvgL(fWtm3_v)abZS?zKBcgeSgfbCO$tx)51~9pIH5StBsQ<1l%!Sh#s{1vq zps2r73!>y-FK-}D&}f7u%d+31qkV*v#IRsIxg=dbTRp4l)HZ#fz3Tclpax*4@K?Mq zE?p!NxCV$WDgeGWFr*dk#Wa25G5?LSEVQuVU*YVVr)N=t3_b%Y7_?jy7N~)T?Z0ur z*}Oi28l1#D{X*(yaq)o&|}Om#w~F6CZ$YE(D@^^*0satvKo1&OBVznv9xSP
JN5nX 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
Tj1!v+$xtx*dI+GHLe_`0!x-QO(~;6cfZ-l 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%0AKWfE$qTJhrf;L-7QRzS1a7;?kZ7Am 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

y3E z293 ztf@XDj!67oeWb{A{xj#CC``F#BA9MS=ZQP`IvDO)4qXoWVoNNSFz<(Dl6K^O{vYv% BkMRHi literal 0 HcmV?d00001 diff --git a/java/src/org/apache/log4j/Appender.java b/java/src/org/apache/log4j/Appender.java deleted file mode 100644 index 42ca4b8..0000000 --- a/java/src/org/apache/log4j/Appender.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.spi.Filter; -import org.apache.log4j.spi.ErrorHandler; -import org.apache.log4j.spi.LoggingEvent; - -/** - Implement this interface for your own strategies for outputting log - statements. - - @author Ceki Gülcü -*/ -public interface Appender { - - /** - Add a filter to the end of the filter list. - - @since 0.9.0 - */ - void addFilter(Filter newFilter); - - /** - Returns the head Filter. The Filters are organized in a linked list - and so all Filters on this Appender are available through the result. - - @return the head Filter or null, if no Filters are present - @since 1.1 - */ - public - Filter getFilter(); - - /** - Clear the list of filters by removing all the filters in it. - - @since 0.9.0 - */ - public - void clearFilters(); - - /** - Release any resources allocated within the appender such as file - handles, network connections, etc. - -

It is a programming error to append to a closed appender. - - @since 0.8.4 - */ - public - void close(); - - /** - Log in Appender specific way. When appropriate, - Loggers will call the doAppend method of appender - implementations in order to log. */ - public - void doAppend(LoggingEvent event); - - - /** - Get the name of this appender. - @return name, may be null.*/ - public - String getName(); - - - /** - Set the {@link ErrorHandler} for this appender. - - @since 0.9.0 - */ - public - void setErrorHandler(ErrorHandler errorHandler); - - /** - Returns the {@link ErrorHandler} for this appender. - - @since 1.1 - */ - public - ErrorHandler getErrorHandler(); - - /** - Set the {@link Layout} for this appender. - - @since 0.8.1 - */ - public - void setLayout(Layout layout); - - /** - Returns this appenders layout. - - @since 1.1 - */ - public - Layout getLayout(); - - - /** - Set the name of this appender. The name is used by other - components to identify this appender. - - @since 0.8.1 - */ - public - void setName(String name); - - /** - Configurators call this method to determine if the appender - requires a layout. If this method returns true, - meaning that layout is required, then the configurator will - configure an layout using the configuration information at its - disposal. If this method returns false, meaning that - a layout is not required, then layout configuration will be - skipped even if there is available layout configuration - information at the disposal of the configurator.. - -

In the rather exceptional case, where the appender - implementation admits a layout but can also work without it, then - the appender should return true. - - @since 0.8.4 */ - public - boolean requiresLayout(); -} diff --git a/java/src/org/apache/log4j/AppenderSkeleton.java b/java/src/org/apache/log4j/AppenderSkeleton.java deleted file mode 100644 index d4d2a5a..0000000 --- a/java/src/org/apache/log4j/AppenderSkeleton.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.spi.Filter; -import org.apache.log4j.spi.ErrorHandler; -import org.apache.log4j.spi.OptionHandler; -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.helpers.OnlyOnceErrorHandler; -import org.apache.log4j.helpers.LogLog; - - -/** - * Abstract superclass of the other appenders in the package. - * - * This class provides the code for common functionality, such as - * support for threshold filtering and support for general filters. - * - * @since 0.8.1 - * @author Ceki Gülcü - * */ -public abstract class AppenderSkeleton implements Appender, OptionHandler { - - /** The layout variable does not need to be set if the appender - implementation has its own layout. */ - protected Layout layout; - - /** Appenders are named. */ - protected String name; - - /** - There is no level threshold filtering by default. */ - protected Priority threshold; - - /** - It is assumed and enforced that errorHandler is never null. - */ - protected ErrorHandler errorHandler = new OnlyOnceErrorHandler(); - - /** The first filter in the filter chain. Set to null - initially. */ - protected Filter headFilter; - /** The last filter in the filter chain. */ - protected Filter tailFilter; - - /** - Is this appender closed? - */ - protected boolean closed = false; - - /** - * Create new instance. - */ - public AppenderSkeleton() { - super(); - } - - /** - * Create new instance. - * Provided for compatibility with log4j 1.3. - * - * @param isActive true if appender is ready for use upon construction. - * Not used in log4j 1.2.x. - * @since 1.2.15 - */ - protected AppenderSkeleton(final boolean isActive) { - super(); - } - - - - /** - Derived appenders should override this method if option structure - requires it. */ - public - void activateOptions() { - } - - - /** - Add a filter to end of the filter list. - - @since 0.9.0 - */ - public - void addFilter(Filter newFilter) { - if(headFilter == null) { - headFilter = tailFilter = newFilter; - } else { - tailFilter.setNext(newFilter); - tailFilter = newFilter; - } - } - - /** - Subclasses of AppenderSkeleton should implement this - method to perform actual logging. See also {@link #doAppend - AppenderSkeleton.doAppend} method. - - @since 0.9.0 - */ - abstract - protected - void append(LoggingEvent event); - - - /** - Clear the filters chain. - - @since 0.9.0 */ - public - void clearFilters() { - headFilter = tailFilter = null; - } - - /** - Finalize this appender by calling the derived class' - close method. - - @since 0.8.4 */ - public - void finalize() { - // An appender might be closed then garbage collected. There is no - // point in closing twice. - if(this.closed) - return; - - LogLog.debug("Finalizing appender named ["+name+"]."); - close(); - } - - - /** - Return the currently set {@link ErrorHandler} for this - Appender. - - @since 0.9.0 */ - public - ErrorHandler getErrorHandler() { - return this.errorHandler; - } - - - /** - Returns the head Filter. - - @since 1.1 - */ - public - Filter getFilter() { - return headFilter; - } - - /** - Return the first filter in the filter chain for this - Appender. The return value may be null if no is - filter is set. - - */ - public - final - Filter getFirstFilter() { - return headFilter; - } - - /** - Returns the layout of this appender. The value may be null. - */ - public - Layout getLayout() { - return layout; - } - - - /** - Returns the name of this appender. - @return name, may be null. - */ - public - final - String getName() { - return this.name; - } - - /** - Returns this appenders threshold level. See the {@link - #setThreshold} method for the meaning of this option. - - @since 1.1 */ - public - Priority getThreshold() { - return threshold; - } - - - /** - Check whether the message level is below the appender's - threshold. If there is no threshold set, then the return value is - always true. - - */ - public - boolean isAsSevereAsThreshold(Priority priority) { - return ((threshold == null) || priority.isGreaterOrEqual(threshold)); - } - - - /** - * This method performs threshold checks and invokes filters before - * delegating actual logging to the subclasses specific {@link - * AppenderSkeleton#append} method. - * */ - public - synchronized - void doAppend(LoggingEvent event) { - if(closed) { - LogLog.error("Attempted to append to closed appender named ["+name+"]."); - return; - } - - if(!isAsSevereAsThreshold(event.getLevel())) { - return; - } - - Filter f = this.headFilter; - - FILTER_LOOP: - while(f != null) { - switch(f.decide(event)) { - case Filter.DENY: return; - case Filter.ACCEPT: break FILTER_LOOP; - case Filter.NEUTRAL: f = f.getNext(); - } - } - - this.append(event); - } - - /** - Set the {@link ErrorHandler} for this Appender. - @since 0.9.0 - */ - public - synchronized - void setErrorHandler(ErrorHandler eh) { - if(eh == null) { - // We do not throw exception here since the cause is probably a - // bad config file. - LogLog.warn("You have tried to set a null error-handler."); - } else { - this.errorHandler = eh; - } - } - - /** - Set the layout for this appender. Note that some appenders have - their own (fixed) layouts or do not use one. For example, the - {@link org.apache.log4j.net.SocketAppender} ignores the layout set - here. - */ - public - void setLayout(Layout layout) { - this.layout = layout; - } - - - /** - Set the name of this Appender. - */ - public - void setName(String name) { - this.name = name; - } - - - /** - Set the threshold level. All log events with lower level - than the threshold level are ignored by the appender. - -

In configuration files this option is specified by setting the - value of the Threshold option to a level - string, such as "DEBUG", "INFO" and so on. - - @since 0.8.3 */ - public - void setThreshold(Priority threshold) { - this.threshold = threshold; - } -} diff --git a/java/src/org/apache/log4j/AsyncAppender.java b/java/src/org/apache/log4j/AsyncAppender.java deleted file mode 100644 index 214ffa7..0000000 --- a/java/src/org/apache/log4j/AsyncAppender.java +++ /dev/null @@ -1,596 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contibutors: Aaron Greenhouse -// Thomas Tuft Muller -package org.apache.log4j; - -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.apache.log4j.helpers.AppenderAttachableImpl; -import org.apache.log4j.spi.AppenderAttachable; -import org.apache.log4j.spi.LoggingEvent; - - -/** - * The AsyncAppender lets users log events asynchronously. - *

- *

- * The AsyncAppender will collect the events sent to it and then dispatch them - * to all the appenders that are attached to it. You can attach multiple - * appenders to an AsyncAppender. - *

- *

- *

- * The AsyncAppender uses a separate thread to serve the events in its buffer. - *

- *

- * Important note: The AsyncAppender can only be script - * configured using the {@link org.apache.log4j.xml.DOMConfigurator}. - *

- * - * @author Ceki Gülcü - * @author Curt Arnold - * @since 0.9.1 - */ -public class AsyncAppender extends AppenderSkeleton - implements AppenderAttachable { - /** - * The default buffer size is set to 128 events. - */ - public static final int DEFAULT_BUFFER_SIZE = 128; - - /** - * Event buffer, also used as monitor to protect itself and - * discardMap from simulatenous modifications. - */ - private final List buffer = new ArrayList(); - - /** - * Map of DiscardSummary objects keyed by logger name. - */ - private final Map discardMap = new HashMap(); - - /** - * Buffer size. - */ - private int bufferSize = DEFAULT_BUFFER_SIZE; - - /** Nested appenders. */ - AppenderAttachableImpl aai; - - /** - * Nested appenders. - */ - private final AppenderAttachableImpl appenders; - - /** - * Dispatcher. - */ - private final Thread dispatcher; - - /** - * Should location info be included in dispatched messages. - */ - private boolean locationInfo = false; - - /** - * Does appender block when buffer is full. - */ - private boolean blocking = true; - - /** - * Create new instance. - */ - public AsyncAppender() { - appenders = new AppenderAttachableImpl(); - - // - // only set for compatibility - aai = appenders; - - dispatcher = - new Thread(new Dispatcher(this, buffer, discardMap, appenders)); - - // It is the user's responsibility to close appenders before - // exiting. - dispatcher.setDaemon(true); - - // set the dispatcher priority to lowest possible value - // dispatcher.setPriority(Thread.MIN_PRIORITY); - dispatcher.setName("AsyncAppender-Dispatcher-" + dispatcher.getName()); - dispatcher.start(); - } - - /** - * Add appender. - * - * @param newAppender appender to add, may not be null. - */ - public void addAppender(final Appender newAppender) { - synchronized (appenders) { - appenders.addAppender(newAppender); - } - } - - /** - * {@inheritDoc} - */ - public void append(final LoggingEvent event) { - // - // if dispatcher thread has died then - // append subsequent events synchronously - // See bug 23021 - if ((dispatcher == null) || !dispatcher.isAlive() || (bufferSize <= 0)) { - synchronized (appenders) { - appenders.appendLoopOnAppenders(event); - } - - return; - } - - // Set the NDC and thread name for the calling thread as these - // LoggingEvent fields were not set at event creation time. - event.getNDC(); - event.getThreadName(); - // Get a copy of this thread's MDC. - event.getMDCCopy(); - if (locationInfo) { - event.getLocationInformation(); - } - event.getRenderedMessage(); - event.getThrowableStrRep(); - - synchronized (buffer) { - while (true) { - int previousSize = buffer.size(); - - if (previousSize < bufferSize) { - buffer.add(event); - - // - // if buffer had been empty - // signal all threads waiting on buffer - // to check their conditions. - // - if (previousSize == 0) { - buffer.notifyAll(); - } - - break; - } - - // - // Following code is only reachable if buffer is full - // - // - // if blocking and thread is not already interrupted - // and not the dispatcher then - // wait for a buffer notification - boolean discard = true; - if (blocking - && !Thread.interrupted() - && Thread.currentThread() != dispatcher) { - try { - buffer.wait(); - discard = false; - } catch (InterruptedException e) { - // - // reset interrupt status so - // calling code can see interrupt on - // their next wait or sleep. - Thread.currentThread().interrupt(); - } - } - - // - // if blocking is false or thread has been interrupted - // add event to discard map. - // - if (discard) { - String loggerName = event.getLoggerName(); - DiscardSummary summary = (DiscardSummary) discardMap.get(loggerName); - - if (summary == null) { - summary = new DiscardSummary(event); - discardMap.put(loggerName, summary); - } else { - summary.add(event); - } - - break; - } - } - } - } - - /** - * Close this AsyncAppender by interrupting the dispatcher - * thread which will process all pending events before exiting. - */ - public void close() { - /** - * Set closed flag and notify all threads to check their conditions. - * Should result in dispatcher terminating. - */ - synchronized (buffer) { - closed = true; - buffer.notifyAll(); - } - - try { - dispatcher.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - org.apache.log4j.helpers.LogLog.error( - "Got an InterruptedException while waiting for the " - + "dispatcher to finish.", e); - } - - // - // close all attached appenders. - // - synchronized (appenders) { - Enumeration iter = appenders.getAllAppenders(); - - if (iter != null) { - while (iter.hasMoreElements()) { - Object next = iter.nextElement(); - - if (next instanceof Appender) { - ((Appender) next).close(); - } - } - } - } - } - - /** - * Get iterator over attached appenders. - * @return iterator or null if no attached appenders. - */ - public Enumeration getAllAppenders() { - synchronized (appenders) { - return appenders.getAllAppenders(); - } - } - - /** - * Get appender by name. - * - * @param name name, may not be null. - * @return matching appender or null. - */ - public Appender getAppender(final String name) { - synchronized (appenders) { - return appenders.getAppender(name); - } - } - - /** - * Gets whether the location of the logging request call - * should be captured. - * - * @return the current value of the LocationInfo option. - */ - public boolean getLocationInfo() { - return locationInfo; - } - - /** - * Determines if specified appender is attached. - * @param appender appender. - * @return true if attached. - */ - public boolean isAttached(final Appender appender) { - synchronized (appenders) { - return appenders.isAttached(appender); - } - } - - /** - * {@inheritDoc} - */ - public boolean requiresLayout() { - return false; - } - - /** - * Removes and closes all attached appenders. - */ - public void removeAllAppenders() { - synchronized (appenders) { - appenders.removeAllAppenders(); - } - } - - /** - * Removes an appender. - * @param appender appender to remove. - */ - public void removeAppender(final Appender appender) { - synchronized (appenders) { - appenders.removeAppender(appender); - } - } - - /** - * Remove appender by name. - * @param name name. - */ - public void removeAppender(final String name) { - synchronized (appenders) { - appenders.removeAppender(name); - } - } - - /** - * The LocationInfo option takes a boolean value. By default, it is - * set to false which means there will be no effort to extract the location - * information related to the event. As a result, the event that will be - * ultimately logged will likely to contain the wrong location information - * (if present in the log format). - *

- *

- * Location information extraction is comparatively very slow and should be - * avoided unless performance is not a concern. - *

- * @param flag true if location information should be extracted. - */ - public void setLocationInfo(final boolean flag) { - locationInfo = flag; - } - - /** - * Sets the number of messages allowed in the event buffer - * before the calling thread is blocked (if blocking is true) - * or until messages are summarized and discarded. Changing - * the size will not affect messages already in the buffer. - * - * @param size buffer size, must be positive. - */ - public void setBufferSize(final int size) { - // - // log4j 1.2 would throw exception if size was negative - // and deadlock if size was zero. - // - if (size < 0) { - throw new java.lang.NegativeArraySizeException("size"); - } - - synchronized (buffer) { - // - // don't let size be zero. - // - bufferSize = (size < 1) ? 1 : size; - buffer.notifyAll(); - } - } - - /** - * Gets the current buffer size. - * @return the current value of the BufferSize option. - */ - public int getBufferSize() { - return bufferSize; - } - - /** - * Sets whether appender should wait if there is no - * space available in the event buffer or immediately return. - * - * @since 1.2.14 - * @param value true if appender should wait until available space in buffer. - */ - public void setBlocking(final boolean value) { - synchronized (buffer) { - blocking = value; - buffer.notifyAll(); - } - } - - /** - * Gets whether appender should block calling thread when buffer is full. - * If false, messages will be counted by logger and a summary - * message appended after the contents of the buffer have been appended. - * - * @since 1.2.14 - * @return true if calling thread will be blocked when buffer is full. - */ - public boolean getBlocking() { - return blocking; - } - - /** - * Summary of discarded logging events for a logger. - */ - private static final class DiscardSummary { - /** - * First event of the highest severity. - */ - private LoggingEvent maxEvent; - - /** - * Total count of messages discarded. - */ - private int count; - - /** - * Create new instance. - * - * @param event event, may not be null. - */ - public DiscardSummary(final LoggingEvent event) { - maxEvent = event; - count = 1; - } - - /** - * Add discarded event to summary. - * - * @param event event, may not be null. - */ - public void add(final LoggingEvent event) { - if (event.getLevel().toInt() > maxEvent.getLevel().toInt()) { - maxEvent = event; - } - - count++; - } - - /** - * Create event with summary information. - * - * @return new event. - */ - public LoggingEvent createEvent() { - String msg = - MessageFormat.format( - "Discarded {0} messages due to full event buffer including: {1}", - new Object[] { new Integer(count), maxEvent.getMessage() }); - - return new LoggingEvent( - "org.apache.log4j.AsyncAppender.DONT_REPORT_LOCATION", - Logger.getLogger(maxEvent.getLoggerName()), - maxEvent.getLevel(), - msg, - null); - } - } - - /** - * Event dispatcher. - */ - private static class Dispatcher implements Runnable { - /** - * Parent AsyncAppender. - */ - private final AsyncAppender parent; - - /** - * Event buffer. - */ - private final List buffer; - - /** - * Map of DiscardSummary keyed by logger name. - */ - private final Map discardMap; - - /** - * Wrapped appenders. - */ - private final AppenderAttachableImpl appenders; - - /** - * Create new instance of dispatcher. - * - * @param parent parent AsyncAppender, may not be null. - * @param buffer event buffer, may not be null. - * @param discardMap discard map, may not be null. - * @param appenders appenders, may not be null. - */ - public Dispatcher( - final AsyncAppender parent, final List buffer, final Map discardMap, - final AppenderAttachableImpl appenders) { - - this.parent = parent; - this.buffer = buffer; - this.appenders = appenders; - this.discardMap = discardMap; - } - - /** - * {@inheritDoc} - */ - public void run() { - boolean isActive = true; - - // - // if interrupted (unlikely), end thread - // - try { - // - // loop until the AsyncAppender is closed. - // - while (isActive) { - LoggingEvent[] events = null; - - // - // extract pending events while synchronized - // on buffer - // - synchronized (buffer) { - int bufferSize = buffer.size(); - isActive = !parent.closed; - - while ((bufferSize == 0) && isActive) { - buffer.wait(); - bufferSize = buffer.size(); - isActive = !parent.closed; - } - - if (bufferSize > 0) { - events = new LoggingEvent[bufferSize + discardMap.size()]; - buffer.toArray(events); - - // - // add events due to buffer overflow - // - int index = bufferSize; - - for ( - Iterator iter = discardMap.values().iterator(); - iter.hasNext();) { - events[index++] = ((DiscardSummary) iter.next()).createEvent(); - } - - // - // clear buffer and discard map - // - buffer.clear(); - discardMap.clear(); - - // - // allow blocked appends to continue - buffer.notifyAll(); - } - } - - // - // process events after lock on buffer is released. - // - if (events != null) { - for (int i = 0; i < events.length; i++) { - synchronized (appenders) { - appenders.appendLoopOnAppenders(events[i]); - } - } - } - } - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } -} diff --git a/java/src/org/apache/log4j/BasicConfigurator.java b/java/src/org/apache/log4j/BasicConfigurator.java deleted file mode 100644 index 2d859cf..0000000 --- a/java/src/org/apache/log4j/BasicConfigurator.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contibutors: "Luke Blanshard" -// "Mark DONSZELMANN" -// "Muly Oved" - -package org.apache.log4j; - - -/** - Use this class to quickly configure the package. - -

For file based configuration see {@link - PropertyConfigurator}. For XML based configuration see {@link - org.apache.log4j.xml.DOMConfigurator DOMConfigurator}. - - @since 0.8.1 - @author Ceki Gülcü */ -public class BasicConfigurator { - - protected BasicConfigurator() { - } - - /** - Add a {@link ConsoleAppender} that uses {@link PatternLayout} - using the {@link PatternLayout#TTCC_CONVERSION_PATTERN} and - prints to System.out to the root category. */ - static - public - void configure() { - Logger root = Logger.getRootLogger(); - root.addAppender(new ConsoleAppender( - new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN))); - } - - /** - Add appender to the root category. - @param appender The appender to add to the root category. - */ - static - public - void configure(Appender appender) { - Logger root = Logger.getRootLogger(); - root.addAppender(appender); - } - - /** - Reset the default hierarchy to its defaut. It is equivalent to - calling - Category.getDefaultHierarchy().resetConfiguration(). - - See {@link Hierarchy#resetConfiguration()} for more details. */ - public - static - void resetConfiguration() { - LogManager.resetConfiguration(); - } -} diff --git a/java/src/org/apache/log4j/Category.java b/java/src/org/apache/log4j/Category.java deleted file mode 100644 index e0cb561..0000000 --- a/java/src/org/apache/log4j/Category.java +++ /dev/null @@ -1,1062 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contibutors: Alex Blewitt -// Markus Oestreicher -// Frank Hoering -// Nelson Minar -// Jim Cakalic -// Avy Sharell -// Ciaran Treanor -// Jeff Turner -// Michael Horwitz -// Calvin Chan -// Aaron Greenhouse -// Beat Meier -// Colin Sampaleanu - -package org.apache.log4j; - -import org.apache.log4j.spi.AppenderAttachable; -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.spi.LoggerRepository; -import org.apache.log4j.spi.HierarchyEventListener; -import org.apache.log4j.helpers.NullEnumeration; -import org.apache.log4j.helpers.AppenderAttachableImpl; - -import java.util.Enumeration; -import java.util.MissingResourceException; -import java.util.ResourceBundle; -import java.util.Vector; - - -/** - * This class has been deprecated and - * replaced by the {@link Logger} subclass. It - * will be kept around to preserve backward compatibility until mid - * 2003. - * - *

Logger is a subclass of Category, i.e. it extends - * Category. In other words, a logger is a category. Thus, - * all operations that can be performed on a category can be - * performed on a logger. Internally, whenever log4j is asked to - * produce a Category object, it will instead produce a Logger - * object. Log4j 1.2 will never produce Category objects but - * only Logger instances. In order to preserve backward - * compatibility, methods that previously accepted category objects - * still continue to accept category objects. - * - *

For example, the following are all legal and will work as - * expected. - * -

-       // Deprecated form:
-       Category cat = Category.getInstance("foo.bar")
-   
-       // Preferred form for retrieving loggers:
-       Logger logger = Logger.getLogger("foo.bar")
-   
- - *

The first form is deprecated and should be avoided. - * - *

There is absolutely no need for new client code to use or - * refer to the Category class. Whenever possible, - * please avoid referring to it or using it. - * - *

- * See the document entitled preparing - * for log4j 1.3 for a more detailed discussion. - * - * @author Ceki Gülcü - * @author Anders Kristensen - */ -public class Category implements AppenderAttachable { - - /** - The hierarchy where categories are attached to by default. - */ - //static - //public - //final Hierarchy defaultHierarchy = new Hierarchy(new - // RootCategory(Level.DEBUG)); - - /** - The name of this category. - */ - protected String name; - - /** - The assigned level of this category. The - level variable need not be assigned a value in - which case it is inherited form the hierarchy. */ - volatile protected Level level; - - /** - The parent of this category. All categories have at least one - ancestor which is the root category. */ - volatile protected Category parent; - - /** - The fully qualified name of the Category class. See also the - getFQCN method. */ - private static final String FQCN = Category.class.getName(); - - protected ResourceBundle resourceBundle; - - // Categories need to know what Hierarchy they are in - protected LoggerRepository repository; - - - AppenderAttachableImpl aai; - - /** Additivity is set to true by default, that is children inherit - the appenders of their ancestors by default. If this variable is - set to false then the appenders found in the - ancestors of this category are not used. However, the children - of this category will inherit its appenders, unless the children - have their additivity flag set to false too. See - the user manual for more details. */ - protected boolean additive = true; - - /** - This constructor created a new Category instance and - sets its name. - -

It is intended to be used by sub-classes only. You should not - create categories directly. - - @param name The name of the category. - */ - protected - Category(String name) { - this.name = name; - } - - /** - Add newAppender to the list of appenders of this - Category instance. - -

If newAppender is already in the list of - appenders, then it won't be added again. - */ - synchronized - public - void addAppender(Appender newAppender) { - if(aai == null) { - aai = new AppenderAttachableImpl(); - } - aai.addAppender(newAppender); - repository.fireAddAppenderEvent(this, newAppender); - } - - /** - If assertion parameter is false, then - logs msg as an {@link #error(Object) error} statement. - -

The assert method has been renamed to - assertLog because assert is a language - reserved word in JDK 1.4. - - @param assertion - @param msg The message to print if assertion is - false. - - @since 1.2 */ - public - void assertLog(boolean assertion, String msg) { - if(!assertion) - this.error(msg); - } - - - /** - Call the appenders in the hierrachy starting at - this. If no appenders could be found, emit a - warning. - -

This method calls all the appenders inherited from the - hierarchy circumventing any evaluation of whether to log or not - to log the particular log request. - - @param event the event to log. */ - public - void callAppenders(LoggingEvent event) { - int writes = 0; - - for(Category c = this; c != null; c=c.parent) { - // Protected against simultaneous call to addAppender, removeAppender,... - synchronized(c) { - if(c.aai != null) { - writes += c.aai.appendLoopOnAppenders(event); - } - if(!c.additive) { - break; - } - } - } - - if(writes == 0) { - repository.emitNoAppenderWarning(this); - } - } - - /** - Close all attached appenders implementing the AppenderAttachable - interface. - @since 1.0 - */ - synchronized - void closeNestedAppenders() { - Enumeration enumeration = this.getAllAppenders(); - if(enumeration != null) { - while(enumeration.hasMoreElements()) { - Appender a = (Appender) enumeration.nextElement(); - if(a instanceof AppenderAttachable) { - a.close(); - } - } - } - } - - /** - Log a message object with the {@link Level#DEBUG DEBUG} level. - -

This method first checks if this category is DEBUG - enabled by comparing the level of this category with the {@link - Level#DEBUG DEBUG} level. If this category is - DEBUG enabled, then it converts the message object - (passed as parameter) to a string by invoking the appropriate - {@link org.apache.log4j.or.ObjectRenderer}. It then proceeds to call all the - registered appenders in this category and also higher in the - hierarchy depending on the value of the additivity flag. - -

WARNING Note that passing a {@link Throwable} to this - method will print the name of the Throwable but no - stack trace. To print a stack trace use the {@link #debug(Object, - Throwable)} form instead. - - @param message the message object to log. */ - public - void debug(Object message) { - if(repository.isDisabled(Level.DEBUG_INT)) - return; - if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) { - forcedLog(FQCN, Level.DEBUG, message, null); - } - } - - - /** - Log a message object with the DEBUG level including - the stack trace of the {@link Throwable} t passed as - parameter. - -

See {@link #debug(Object)} form for more detailed information. - - @param message the message object to log. - @param t the exception to log, including its stack trace. */ - public - void debug(Object message, Throwable t) { - if(repository.isDisabled(Level.DEBUG_INT)) - return; - if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, Level.DEBUG, message, t); - } - - /** - Log a message object with the {@link Level#ERROR ERROR} Level. - -

This method first checks if this category is ERROR - enabled by comparing the level of this category with {@link - Level#ERROR ERROR} Level. If this category is ERROR - enabled, then it converts the message object passed as parameter - to a string by invoking the appropriate {@link - org.apache.log4j.or.ObjectRenderer}. It proceeds to call all the - registered appenders in this category and also higher in the - hierarchy depending on the value of the additivity flag. - -

WARNING Note that passing a {@link Throwable} to this - method will print the name of the Throwable but no - stack trace. To print a stack trace use the {@link #error(Object, - Throwable)} form instead. - - @param message the message object to log */ - public - void error(Object message) { - if(repository.isDisabled(Level.ERROR_INT)) - return; - if(Level.ERROR.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, Level.ERROR, message, null); - } - - /** - Log a message object with the ERROR level including - the stack trace of the {@link Throwable} t passed as - parameter. - -

See {@link #error(Object)} form for more detailed information. - - @param message the message object to log. - @param t the exception to log, including its stack trace. */ - public - void error(Object message, Throwable t) { - if(repository.isDisabled(Level.ERROR_INT)) - return; - if(Level.ERROR.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, Level.ERROR, message, t); - - } - - - /** - If the named category exists (in the default hierarchy) then it - returns a reference to the category, otherwise it returns - null. - - @deprecated Please use {@link LogManager#exists} instead. - - @since 0.8.5 */ - public - static - Logger exists(String name) { - return LogManager.exists(name); - } - - /** - Log a message object with the {@link Level#FATAL FATAL} Level. - -

This method first checks if this category is FATAL - enabled by comparing the level of this category with {@link - Level#FATAL FATAL} Level. If the category is FATAL - enabled, then it converts the message object passed as parameter - to a string by invoking the appropriate - {@link org.apache.log4j.or.ObjectRenderer}. It - proceeds to call all the registered appenders in this category and - also higher in the hierarchy depending on the value of the - additivity flag. - -

WARNING Note that passing a {@link Throwable} to this - method will print the name of the Throwable but no stack trace. To - print a stack trace use the {@link #fatal(Object, Throwable)} form - instead. - - @param message the message object to log */ - public - void fatal(Object message) { - if(repository.isDisabled(Level.FATAL_INT)) - return; - if(Level.FATAL.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, Level.FATAL, message, null); - } - - /** - Log a message object with the FATAL level including - the stack trace of the {@link Throwable} t passed as - parameter. - -

See {@link #fatal(Object)} for more detailed information. - - @param message the message object to log. - @param t the exception to log, including its stack trace. */ - public - void fatal(Object message, Throwable t) { - if(repository.isDisabled(Level.FATAL_INT)) - return; - if(Level.FATAL.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, Level.FATAL, message, t); - } - - - /** - This method creates a new logging event and logs the event - without further checks. */ - protected - void forcedLog(String fqcn, Priority level, Object message, Throwable t) { - callAppenders(new LoggingEvent(fqcn, this, level, message, t)); - } - - - /** - Get the additivity flag for this Category instance. - */ - public - boolean getAdditivity() { - return additive; - } - - /** - Get the appenders contained in this category as an {@link - Enumeration}. If no appenders can be found, then a {@link NullEnumeration} - is returned. - - @return Enumeration An enumeration of the appenders in this category. */ - synchronized - public - Enumeration getAllAppenders() { - if(aai == null) - return NullEnumeration.getInstance(); - else - return aai.getAllAppenders(); - } - - /** - Look for the appender named as name. - -

Return the appender with that name if in the list. Return - null otherwise. */ - synchronized - public - Appender getAppender(String name) { - if(aai == null || name == null) - return null; - - return aai.getAppender(name); - } - - /** - Starting from this category, search the category hierarchy for a - non-null level and return it. Otherwise, return the level of the - root category. - -

The Category class is designed so that this method executes as - quickly as possible. - */ - public - Level getEffectiveLevel() { - for(Category c = this; c != null; c=c.parent) { - if(c.level != null) - return c.level; - } - return null; // If reached will cause an NullPointerException. - } - - /** - * - * @deprecated Please use the the {@link #getEffectiveLevel} method - * instead. - * */ - public - Priority getChainedPriority() { - for(Category c = this; c != null; c=c.parent) { - if(c.level != null) - return c.level; - } - return null; // If reached will cause an NullPointerException. - } - - - /** - Returns all the currently defined categories in the default - hierarchy as an {@link java.util.Enumeration Enumeration}. - -

The root category is not included in the returned - {@link Enumeration}. - - @deprecated Please use {@link LogManager#getCurrentLoggers()} instead. - */ - public - static - Enumeration getCurrentCategories() { - return LogManager.getCurrentLoggers(); - } - - - /** - Return the default Hierarchy instance. - - @deprecated Please use {@link LogManager#getLoggerRepository()} instead. - - @since 1.0 - */ - public - static - LoggerRepository getDefaultHierarchy() { - return LogManager.getLoggerRepository(); - } - - /** - Return the the {@link Hierarchy} where this Category - instance is attached. - - @deprecated Please use {@link #getLoggerRepository} instead. - - @since 1.1 */ - public - LoggerRepository getHierarchy() { - return repository; - } - - /** - Return the the {@link LoggerRepository} where this - Category is attached. - - @since 1.2 */ - public - LoggerRepository getLoggerRepository() { - return repository; - } - - - /** - * @deprecated Make sure to use {@link Logger#getLogger(String)} instead. - */ - public - static - Category getInstance(String name) { - return LogManager.getLogger(name); - } - - /** - * @deprecated Please make sure to use {@link Logger#getLogger(Class)} instead. - */ - public - static - Category getInstance(Class clazz) { - return LogManager.getLogger(clazz); - } - - - /** - Return the category name. */ - public - final - String getName() { - return name; - } - - - /** - Returns the parent of this category. Note that the parent of a - given category may change during the lifetime of the category. - -

The root category will return null. - - @since 1.2 - */ - final - public - Category getParent() { - return this.parent; - } - - - /** - Returns the assigned {@link Level}, if any, for this Category. - - @return Level - the assigned Level, can be null. - */ - final - public - Level getLevel() { - return this.level; - } - - /** - @deprecated Please use {@link #getLevel} instead. - */ - final - public - Level getPriority() { - return this.level; - } - - - /** - * @deprecated Please use {@link Logger#getRootLogger()} instead. - */ - final - public - static - Category getRoot() { - return LogManager.getRootLogger(); - } - - /** - Return the inherited {@link ResourceBundle} for this - category. - -

This method walks the hierarchy to find the appropriate - resource bundle. It will return the resource bundle attached to - the closest ancestor of this category, much like the way - priorities are searched. In case there is no bundle in the - hierarchy then null is returned. - - @since 0.9.0 */ - public - ResourceBundle getResourceBundle() { - for(Category c = this; c != null; c=c.parent) { - if(c.resourceBundle != null) - return c.resourceBundle; - } - // It might be the case that there is no resource bundle - return null; - } - - /** - Returns the string resource coresponding to key in - this category's inherited resource bundle. See also {@link - #getResourceBundle}. - -

If the resource cannot be found, then an {@link #error error} - message will be logged complaining about the missing resource. - */ - protected - String getResourceBundleString(String key) { - ResourceBundle rb = getResourceBundle(); - // This is one of the rare cases where we can use logging in order - // to report errors from within log4j. - if(rb == null) { - //if(!hierarchy.emittedNoResourceBundleWarning) { - //error("No resource bundle has been set for category "+name); - //hierarchy.emittedNoResourceBundleWarning = true; - //} - return null; - } - else { - try { - return rb.getString(key); - } - catch(MissingResourceException mre) { - error("No resource is associated with key \""+key+"\"."); - return null; - } - } - } - - /** - Log a message object with the {@link Level#INFO INFO} Level. - -

This method first checks if this category is INFO - enabled by comparing the level of this category with {@link - Level#INFO INFO} Level. If the category is INFO - enabled, then it converts the message object passed as parameter - to a string by invoking the appropriate - {@link org.apache.log4j.or.ObjectRenderer}. It - proceeds to call all the registered appenders in this category and - also higher in the hierarchy depending on the value of the - additivity flag. - -

WARNING Note that passing a {@link Throwable} to this - method will print the name of the Throwable but no stack trace. To - print a stack trace use the {@link #info(Object, Throwable)} form - instead. - - @param message the message object to log */ - public - void info(Object message) { - if(repository.isDisabled(Level.INFO_INT)) - return; - if(Level.INFO.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, Level.INFO, message, null); - } - - /** - Log a message object with the INFO level including - the stack trace of the {@link Throwable} t passed as - parameter. - -

See {@link #info(Object)} for more detailed information. - - @param message the message object to log. - @param t the exception to log, including its stack trace. */ - public - void info(Object message, Throwable t) { - if(repository.isDisabled(Level.INFO_INT)) - return; - if(Level.INFO.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, Level.INFO, message, t); - } - - /** - Is the appender passed as parameter attached to this category? - */ - public - boolean isAttached(Appender appender) { - if(appender == null || aai == null) - return false; - else { - return aai.isAttached(appender); - } - } - - /** - * Check whether this category is enabled for the DEBUG - * Level. - * - *

This function is intended to lessen the computational cost of - * disabled log debug statements. - * - *

For some cat Category object, when you write, - *

-    *      cat.debug("This is entry number: " + i );
-    *  
- * - *

You incur the cost constructing the message, concatenatiion in - * this case, regardless of whether the message is logged or not. - * - *

If you are worried about speed, then you should write - *

-    * 	 if(cat.isDebugEnabled()) {
-    * 	   cat.debug("This is entry number: " + i );
-    * 	 }
-    *  
- * - *

This way you will not incur the cost of parameter - * construction if debugging is disabled for cat. On - * the other hand, if the cat is debug enabled, you - * will incur the cost of evaluating whether the category is debug - * enabled twice. Once in isDebugEnabled and once in - * the debug. This is an insignificant overhead - * since evaluating a category takes about 1%% of the time it - * takes to actually log. - * - * @return boolean - true if this category is debug - * enabled, false otherwise. - * */ - public - boolean isDebugEnabled() { - if(repository.isDisabled( Level.DEBUG_INT)) - return false; - return Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel()); - } - - /** - Check whether this category is enabled for a given {@link - Level} passed as parameter. - - See also {@link #isDebugEnabled}. - - @return boolean True if this category is enabled for level. - */ - public - boolean isEnabledFor(Priority level) { - if(repository.isDisabled(level.level)) - return false; - return level.isGreaterOrEqual(this.getEffectiveLevel()); - } - - /** - Check whether this category is enabled for the info Level. - See also {@link #isDebugEnabled}. - - @return boolean - true if this category is enabled - for level info, false otherwise. - */ - public - boolean isInfoEnabled() { - if(repository.isDisabled(Level.INFO_INT)) - return false; - return Level.INFO.isGreaterOrEqual(this.getEffectiveLevel()); - } - - - /** - Log a localized message. The user supplied parameter - key is replaced by its localized version from the - resource bundle. - - @see #setResourceBundle - - @since 0.8.4 */ - public - void l7dlog(Priority priority, String key, Throwable t) { - if(repository.isDisabled(priority.level)) { - return; - } - if(priority.isGreaterOrEqual(this.getEffectiveLevel())) { - String msg = getResourceBundleString(key); - // if message corresponding to 'key' could not be found in the - // resource bundle, then default to 'key'. - if(msg == null) { - msg = key; - } - forcedLog(FQCN, priority, msg, t); - } - } - /** - Log a localized and parameterized message. First, the user - supplied key is searched in the resource - bundle. Next, the resulting pattern is formatted using - {@link java.text.MessageFormat#format(String,Object[])} method with the - user supplied object array params. - - @since 0.8.4 - */ - public - void l7dlog(Priority priority, String key, Object[] params, Throwable t) { - if(repository.isDisabled(priority.level)) { - return; - } - if(priority.isGreaterOrEqual(this.getEffectiveLevel())) { - String pattern = getResourceBundleString(key); - String msg; - if(pattern == null) - msg = key; - else - msg = java.text.MessageFormat.format(pattern, params); - forcedLog(FQCN, priority, msg, t); - } - } - - /** - This generic form is intended to be used by wrappers. - */ - public - void log(Priority priority, Object message, Throwable t) { - if(repository.isDisabled(priority.level)) { - return; - } - if(priority.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, priority, message, t); - } - - /** - This generic form is intended to be used by wrappers. - */ - public - void log(Priority priority, Object message) { - if(repository.isDisabled(priority.level)) { - return; - } - if(priority.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, priority, message, null); - } - - /** - - This is the most generic printing method. It is intended to be - invoked by wrapper classes. - - @param callerFQCN The wrapper class' fully qualified class name. - @param level The level of the logging request. - @param message The message of the logging request. - @param t The throwable of the logging request, may be null. */ - public - void log(String callerFQCN, Priority level, Object message, Throwable t) { - if(repository.isDisabled(level.level)) { - return; - } - if(level.isGreaterOrEqual(this.getEffectiveLevel())) { - forcedLog(callerFQCN, level, message, t); - } - } - - /** - * LoggerRepository forgot the fireRemoveAppenderEvent method, - * if using the stock Hierarchy implementation, then call its fireRemove. - * Custom repositories can implement HierarchyEventListener if they - * want remove notifications. - * @param appender appender, may be null. - */ - private void fireRemoveAppenderEvent(final Appender appender) { - if (appender != null) { - if (repository instanceof Hierarchy) { - ((Hierarchy) repository).fireRemoveAppenderEvent(this, appender); - } else if (repository instanceof HierarchyEventListener) { - ((HierarchyEventListener) repository).removeAppenderEvent(this, appender); - } - } - } - - /** - Remove all previously added appenders from this Category - instance. - -

This is useful when re-reading configuration information. - */ - synchronized - public - void removeAllAppenders() { - if(aai != null) { - Vector appenders = new Vector(); - for (Enumeration iter = aai.getAllAppenders(); iter != null && iter.hasMoreElements();) { - appenders.add(iter.nextElement()); - } - aai.removeAllAppenders(); - for(Enumeration iter = appenders.elements(); iter.hasMoreElements();) { - fireRemoveAppenderEvent((Appender) iter.nextElement()); - } - aai = null; - } - } - - - /** - Remove the appender passed as parameter form the list of appenders. - - @since 0.8.2 - */ - synchronized - public - void removeAppender(Appender appender) { - if(appender == null || aai == null) - return; - boolean wasAttached = aai.isAttached(appender); - aai.removeAppender(appender); - if (wasAttached) { - fireRemoveAppenderEvent(appender); - } - } - - /** - Remove the appender with the name passed as parameter form the - list of appenders. - - @since 0.8.2 */ - synchronized - public - void removeAppender(String name) { - if(name == null || aai == null) return; - Appender appender = aai.getAppender(name); - aai.removeAppender(name); - if (appender != null) { - fireRemoveAppenderEvent(appender); - } - } - - /** - Set the additivity flag for this Category instance. - @since 0.8.1 - */ - public - void setAdditivity(boolean additive) { - this.additive = additive; - } - - /** - Only the Hiearchy class can set the hiearchy of a - category. Default package access is MANDATORY here. */ - final - void setHierarchy(LoggerRepository repository) { - this.repository = repository; - } - - /** - Set the level of this Category. If you are passing any of - Level.DEBUG, Level.INFO, - Level.WARN, Level.ERROR, - Level.FATAL as a parameter, you need to case them as - Level. - -

As in

    logger.setLevel((Level) Level.DEBUG); 
- - -

Null values are admitted. */ - public - void setLevel(Level level) { - this.level = level; - } - - - /** - Set the level of this Category. - -

Null values are admitted. - - @deprecated Please use {@link #setLevel} instead. - */ - public - void setPriority(Priority priority) { - this.level = (Level) priority; - } - - - /** - Set the resource bundle to be used with localized logging - methods {@link #l7dlog(Priority,String,Throwable)} and {@link - #l7dlog(Priority,String,Object[],Throwable)}. - - @since 0.8.4 - */ - public - void setResourceBundle(ResourceBundle bundle) { - resourceBundle = bundle; - } - - /** - Calling this method will safely close and remove all - appenders in all the categories including root contained in the - default hierachy. - -

Some appenders such as {@link org.apache.log4j.net.SocketAppender} - and {@link AsyncAppender} need to be closed before the - application exists. Otherwise, pending logging events might be - lost. - -

The shutdown method is careful to close nested - appenders before closing regular appenders. This is allows - configurations where a regular appender is attached to a category - and again to a nested appender. - - @deprecated Please use {@link LogManager#shutdown()} instead. - - @since 1.0 - */ - public - static - void shutdown() { - LogManager.shutdown(); - } - - - /** - Log a message object with the {@link Level#WARN WARN} Level. - -

This method first checks if this category is WARN - enabled by comparing the level of this category with {@link - Level#WARN WARN} Level. If the category is WARN - enabled, then it converts the message object passed as parameter - to a string by invoking the appropriate - {@link org.apache.log4j.or.ObjectRenderer}. It - proceeds to call all the registered appenders in this category and - also higher in the hieararchy depending on the value of the - additivity flag. - -

WARNING Note that passing a {@link Throwable} to this - method will print the name of the Throwable but no stack trace. To - print a stack trace use the {@link #warn(Object, Throwable)} form - instead.

- - @param message the message object to log. */ - public - void warn(Object message) { - if(repository.isDisabled( Level.WARN_INT)) - return; - - if(Level.WARN.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, Level.WARN, message, null); - } - - /** - Log a message with the WARN level including the - stack trace of the {@link Throwable} t passed as - parameter. - -

See {@link #warn(Object)} for more detailed information. - - @param message the message object to log. - @param t the exception to log, including its stack trace. */ - public - void warn(Object message, Throwable t) { - if(repository.isDisabled(Level.WARN_INT)) - return; - if(Level.WARN.isGreaterOrEqual(this.getEffectiveLevel())) - forcedLog(FQCN, Level.WARN, message, t); - } -} diff --git a/java/src/org/apache/log4j/CategoryKey.java b/java/src/org/apache/log4j/CategoryKey.java deleted file mode 100644 index fe0fff7..0000000 --- a/java/src/org/apache/log4j/CategoryKey.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -/** - CategoryKey is a wrapper for String that apparently accellerated - hash table lookup in early JVM's. - @author Ceki Gülcü -*/ -class CategoryKey { - - String name; - int hashCache; - - CategoryKey(String name) { - this.name = name; - hashCache = name.hashCode(); - } - - final - public - int hashCode() { - return hashCache; - } - - final - public - boolean equals(Object rArg) { - if(this == rArg) - return true; - - if(rArg != null && CategoryKey.class == rArg.getClass()) - return name.equals(((CategoryKey)rArg ).name); - else - return false; - } -} diff --git a/java/src/org/apache/log4j/ConsoleAppender.java b/java/src/org/apache/log4j/ConsoleAppender.java deleted file mode 100644 index b44fd5f..0000000 --- a/java/src/org/apache/log4j/ConsoleAppender.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import java.io.IOException; -import java.io.OutputStream; -import org.apache.log4j.helpers.LogLog; - -/** - * ConsoleAppender appends log events to System.out or - * System.err using a layout specified by the user. The - * default target is System.out. - * - * @author Ceki Gülcü - * @author Curt Arnold - * @since 1.1 */ -public class ConsoleAppender extends WriterAppender { - - public static final String SYSTEM_OUT = "System.out"; - public static final String SYSTEM_ERR = "System.err"; - - protected String target = SYSTEM_OUT; - - /** - * Determines if the appender honors reassignments of System.out - * or System.err made after configuration. - */ - private boolean follow = false; - - /** - * Constructs an unconfigured appender. - */ - public ConsoleAppender() { - } - - /** - * Creates a configured appender. - * - * @param layout layout, may not be null. - */ - public ConsoleAppender(Layout layout) { - this(layout, SYSTEM_OUT); - } - - /** - * Creates a configured appender. - * @param layout layout, may not be null. - * @param target target, either "System.err" or "System.out". - */ - public ConsoleAppender(Layout layout, String target) { - setLayout(layout); - setTarget(target); - activateOptions(); - } - - /** - * Sets the value of the Target option. Recognized values - * are "System.out" and "System.err". Any other value will be - * ignored. - * */ - public - void setTarget(String value) { - String v = value.trim(); - - if (SYSTEM_OUT.equalsIgnoreCase(v)) { - target = SYSTEM_OUT; - } else if (SYSTEM_ERR.equalsIgnoreCase(v)) { - target = SYSTEM_ERR; - } else { - targetWarn(value); - } - } - - /** - * Returns the current value of the Target property. The - * default value of the option is "System.out". - * - * See also {@link #setTarget}. - * */ - public - String getTarget() { - return target; - } - - /** - * Sets whether the appender honors reassignments of System.out - * or System.err made after configuration. - * @param newValue if true, appender will use value of System.out or - * System.err in force at the time when logging events are appended. - * @since 1.2.13 - */ - public final void setFollow(final boolean newValue) { - follow = newValue; - } - - /** - * Gets whether the appender honors reassignments of System.out - * or System.err made after configuration. - * @return true if appender will use value of System.out or - * System.err in force at the time when logging events are appended. - * @since 1.2.13 - */ - public final boolean getFollow() { - return follow; - } - - void targetWarn(String val) { - LogLog.warn("["+val+"] should be System.out or System.err."); - LogLog.warn("Using previously set target, System.out by default."); - } - - /** - * Prepares the appender for use. - */ - public void activateOptions() { - if (follow) { - if (target.equals(SYSTEM_ERR)) { - setWriter(createWriter(new SystemErrStream())); - } else { - setWriter(createWriter(new SystemOutStream())); - } - } else { - if (target.equals(SYSTEM_ERR)) { - setWriter(createWriter(System.err)); - } else { - setWriter(createWriter(System.out)); - } - } - - super.activateOptions(); - } - - /** - * {@inheritDoc} - */ - protected - final - void closeWriter() { - if (follow) { - super.closeWriter(); - } - } - - - /** - * An implementation of OutputStream that redirects to the - * current System.err. - * - */ - private static class SystemErrStream extends OutputStream { - public SystemErrStream() { - } - - public void close() { - } - - public void flush() { - System.err.flush(); - } - - public void write(final byte[] b) throws IOException { - System.err.write(b); - } - - public void write(final byte[] b, final int off, final int len) - throws IOException { - System.err.write(b, off, len); - } - - public void write(final int b) throws IOException { - System.err.write(b); - } - } - - /** - * An implementation of OutputStream that redirects to the - * current System.out. - * - */ - private static class SystemOutStream extends OutputStream { - public SystemOutStream() { - } - - public void close() { - } - - public void flush() { - System.out.flush(); - } - - public void write(final byte[] b) throws IOException { - System.out.write(b); - } - - public void write(final byte[] b, final int off, final int len) - throws IOException { - System.out.write(b, off, len); - } - - public void write(final int b) throws IOException { - System.out.write(b); - } - } - -} diff --git a/java/src/org/apache/log4j/DailyRollingFileAppender.java b/java/src/org/apache/log4j/DailyRollingFileAppender.java deleted file mode 100644 index 79a3b5c..0000000 --- a/java/src/org/apache/log4j/DailyRollingFileAppender.java +++ /dev/null @@ -1,454 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - - -package org.apache.log4j; - -import java.io.IOException; -import java.io.File; -import java.io.InterruptedIOException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.Locale; - -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.spi.LoggingEvent; - -/** - DailyRollingFileAppender extends {@link FileAppender} so that the - underlying file is rolled over at a user chosen frequency. - - DailyRollingFileAppender has been observed to exhibit - synchronization issues and data loss. The log4j extras - companion includes alternatives which should be considered - for new deployments and which are discussed in the documentation - for org.apache.log4j.rolling.RollingFileAppender. - -

The rolling schedule is specified by the DatePattern - option. This pattern should follow the {@link SimpleDateFormat} - conventions. In particular, you must escape literal text - within a pair of single quotes. A formatted version of the date - pattern is used as the suffix for the rolled file name. - -

For example, if the File option is set to - /foo/bar.log and the DatePattern set to - '.'yyyy-MM-dd, on 2001-02-16 at midnight, the logging - file /foo/bar.log will be copied to - /foo/bar.log.2001-02-16 and logging for 2001-02-17 - will continue in /foo/bar.log until it rolls over - the next day. - -

Is is possible to specify monthly, weekly, half-daily, daily, - hourly, or minutely rollover schedules. - -

- - - - - - - - - - - - - - - - - - - - - - - -
DatePatternRollover scheduleExample
'.'yyyy-MM - Rollover at the beginning of each monthAt midnight of May 31st, 2002 /foo/bar.log will be - copied to /foo/bar.log.2002-05. Logging for the month - of June will be output to /foo/bar.log until it is - also rolled over the next month. - -
'.'yyyy-ww - - Rollover at the first day of each week. The first day of the - week depends on the locale.Assuming the first day of the week is Sunday, on Saturday - midnight, June 9th 2002, the file /foo/bar.log will be - copied to /foo/bar.log.2002-23. Logging for the 24th week - of 2002 will be output to /foo/bar.log until it is - rolled over the next week. - -
'.'yyyy-MM-dd - - Rollover at midnight each day.At midnight, on March 8th, 2002, /foo/bar.log will - be copied to /foo/bar.log.2002-03-08. Logging for the - 9th day of March will be output to /foo/bar.log until - it is rolled over the next day. - -
'.'yyyy-MM-dd-a - - Rollover at midnight and midday of each day.At noon, on March 9th, 2002, /foo/bar.log will be - copied to /foo/bar.log.2002-03-09-AM. Logging for the - afternoon of the 9th will be output to /foo/bar.log - until it is rolled over at midnight. - -
'.'yyyy-MM-dd-HH - - Rollover at the top of every hour.At approximately 11:00.000 o'clock on March 9th, 2002, - /foo/bar.log will be copied to - /foo/bar.log.2002-03-09-10. Logging for the 11th hour - of the 9th of March will be output to /foo/bar.log - until it is rolled over at the beginning of the next hour. - - -
'.'yyyy-MM-dd-HH-mm - - Rollover at the beginning of every minute.At approximately 11:23,000, on March 9th, 2001, - /foo/bar.log will be copied to - /foo/bar.log.2001-03-09-10-22. Logging for the minute - of 11:23 (9th of March) will be output to - /foo/bar.log until it is rolled over the next minute. - -
- -

Do not use the colon ":" character in anywhere in the - DatePattern option. The text before the colon is interpeted - as the protocol specificaion of a URL which is probably not what - you want. - - - @author Eirik Lygre - @author Ceki Gülcü*/ -public class DailyRollingFileAppender extends FileAppender { - - - // The code assumes that the following constants are in a increasing - // sequence. - static final int TOP_OF_TROUBLE=-1; - static final int TOP_OF_MINUTE = 0; - static final int TOP_OF_HOUR = 1; - static final int HALF_DAY = 2; - static final int TOP_OF_DAY = 3; - static final int TOP_OF_WEEK = 4; - static final int TOP_OF_MONTH = 5; - - - /** - The date pattern. By default, the pattern is set to - "'.'yyyy-MM-dd" meaning daily rollover. - */ - private String datePattern = "'.'yyyy-MM-dd"; - - /** - The log file will be renamed to the value of the - scheduledFilename variable when the next interval is entered. For - example, if the rollover period is one hour, the log file will be - renamed to the value of "scheduledFilename" at the beginning of - the next hour. - - The precise time when a rollover occurs depends on logging - activity. - */ - private String scheduledFilename; - - /** - The next time we estimate a rollover should occur. */ - private long nextCheck = System.currentTimeMillis () - 1; - - Date now = new Date(); - - SimpleDateFormat sdf; - - RollingCalendar rc = new RollingCalendar(); - - int checkPeriod = TOP_OF_TROUBLE; - - // The gmtTimeZone is used only in computeCheckPeriod() method. - static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT"); - - - /** - The default constructor does nothing. */ - public DailyRollingFileAppender() { - } - - /** - Instantiate a DailyRollingFileAppender and open the - file designated by filename. The opened filename will - become the ouput destination for this appender. - - */ - public DailyRollingFileAppender (Layout layout, String filename, - String datePattern) throws IOException { - super(layout, filename, true); - this.datePattern = datePattern; - activateOptions(); - } - - /** - The DatePattern takes a string in the same format as - expected by {@link SimpleDateFormat}. This options determines the - rollover schedule. - */ - public void setDatePattern(String pattern) { - datePattern = pattern; - } - - /** Returns the value of the DatePattern option. */ - public String getDatePattern() { - return datePattern; - } - - public void activateOptions() { - super.activateOptions(); - if(datePattern != null && fileName != null) { - now.setTime(System.currentTimeMillis()); - sdf = new SimpleDateFormat(datePattern); - int type = computeCheckPeriod(); - printPeriodicity(type); - rc.setType(type); - File file = new File(fileName); - scheduledFilename = fileName+sdf.format(new Date(file.lastModified())); - - } else { - LogLog.error("Either File or DatePattern options are not set for appender [" - +name+"]."); - } - } - - void printPeriodicity(int type) { - switch(type) { - case TOP_OF_MINUTE: - LogLog.debug("Appender ["+name+"] to be rolled every minute."); - break; - case TOP_OF_HOUR: - LogLog.debug("Appender ["+name - +"] to be rolled on top of every hour."); - break; - case HALF_DAY: - LogLog.debug("Appender ["+name - +"] to be rolled at midday and midnight."); - break; - case TOP_OF_DAY: - LogLog.debug("Appender ["+name - +"] to be rolled at midnight."); - break; - case TOP_OF_WEEK: - LogLog.debug("Appender ["+name - +"] to be rolled at start of week."); - break; - case TOP_OF_MONTH: - LogLog.debug("Appender ["+name - +"] to be rolled at start of every month."); - break; - default: - LogLog.warn("Unknown periodicity for appender ["+name+"]."); - } - } - - - // This method computes the roll over period by looping over the - // periods, starting with the shortest, and stopping when the r0 is - // different from from r1, where r0 is the epoch formatted according - // the datePattern (supplied by the user) and r1 is the - // epoch+nextMillis(i) formatted according to datePattern. All date - // formatting is done in GMT and not local format because the test - // logic is based on comparisons relative to 1970-01-01 00:00:00 - // GMT (the epoch). - - int computeCheckPeriod() { - RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.getDefault()); - // set sate to 1970-01-01 00:00:00 GMT - Date epoch = new Date(0); - if(datePattern != null) { - for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern); - simpleDateFormat.setTimeZone(gmtTimeZone); // do all date formatting in GMT - String r0 = simpleDateFormat.format(epoch); - rollingCalendar.setType(i); - Date next = new Date(rollingCalendar.getNextCheckMillis(epoch)); - String r1 = simpleDateFormat.format(next); - //System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1); - if(r0 != null && r1 != null && !r0.equals(r1)) { - return i; - } - } - } - return TOP_OF_TROUBLE; // Deliberately head for trouble... - } - - /** - Rollover the current file to a new file. - */ - void rollOver() throws IOException { - - /* Compute filename, but only if datePattern is specified */ - if (datePattern == null) { - errorHandler.error("Missing DatePattern option in rollOver()."); - return; - } - - String datedFilename = fileName+sdf.format(now); - // It is too early to roll over because we are still within the - // bounds of the current interval. Rollover will occur once the - // next interval is reached. - if (scheduledFilename.equals(datedFilename)) { - return; - } - - // close current file, and rename it to datedFilename - this.closeFile(); - - File target = new File(scheduledFilename); - if (target.exists()) { - target.delete(); - } - - File file = new File(fileName); - boolean result = file.renameTo(target); - if(result) { - LogLog.debug(fileName +" -> "+ scheduledFilename); - } else { - LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"]."); - } - - try { - // This will also close the file. This is OK since multiple - // close operations are safe. - this.setFile(fileName, true, this.bufferedIO, this.bufferSize); - } - catch(IOException e) { - errorHandler.error("setFile("+fileName+", true) call failed."); - } - scheduledFilename = datedFilename; - } - - /** - * This method differentiates DailyRollingFileAppender from its - * super class. - * - *

Before actually logging, this method will check whether it is - * time to do a rollover. If it is, it will schedule the next - * rollover time and then rollover. - * */ - protected void subAppend(LoggingEvent event) { - long n = System.currentTimeMillis(); - if (n >= nextCheck) { - now.setTime(n); - nextCheck = rc.getNextCheckMillis(now); - try { - rollOver(); - } - catch(IOException ioe) { - if (ioe instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("rollOver() failed.", ioe); - } - } - super.subAppend(event); - } -} - -/** - * RollingCalendar is a helper class to DailyRollingFileAppender. - * Given a periodicity type and the current time, it computes the - * start of the next interval. - * */ -class RollingCalendar extends GregorianCalendar { - private static final long serialVersionUID = -3560331770601814177L; - - int type = DailyRollingFileAppender.TOP_OF_TROUBLE; - - RollingCalendar() { - super(); - } - - RollingCalendar(TimeZone tz, Locale locale) { - super(tz, locale); - } - - void setType(int type) { - this.type = type; - } - - public long getNextCheckMillis(Date now) { - return getNextCheckDate(now).getTime(); - } - - public Date getNextCheckDate(Date now) { - this.setTime(now); - - switch(type) { - case DailyRollingFileAppender.TOP_OF_MINUTE: - this.set(Calendar.SECOND, 0); - this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.MINUTE, 1); - break; - case DailyRollingFileAppender.TOP_OF_HOUR: - this.set(Calendar.MINUTE, 0); - this.set(Calendar.SECOND, 0); - this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.HOUR_OF_DAY, 1); - break; - case DailyRollingFileAppender.HALF_DAY: - this.set(Calendar.MINUTE, 0); - this.set(Calendar.SECOND, 0); - this.set(Calendar.MILLISECOND, 0); - int hour = get(Calendar.HOUR_OF_DAY); - if(hour < 12) { - this.set(Calendar.HOUR_OF_DAY, 12); - } else { - this.set(Calendar.HOUR_OF_DAY, 0); - this.add(Calendar.DAY_OF_MONTH, 1); - } - break; - case DailyRollingFileAppender.TOP_OF_DAY: - this.set(Calendar.HOUR_OF_DAY, 0); - this.set(Calendar.MINUTE, 0); - this.set(Calendar.SECOND, 0); - this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.DATE, 1); - break; - case DailyRollingFileAppender.TOP_OF_WEEK: - this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek()); - this.set(Calendar.HOUR_OF_DAY, 0); - this.set(Calendar.MINUTE, 0); - this.set(Calendar.SECOND, 0); - this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.WEEK_OF_YEAR, 1); - break; - case DailyRollingFileAppender.TOP_OF_MONTH: - this.set(Calendar.DATE, 1); - this.set(Calendar.HOUR_OF_DAY, 0); - this.set(Calendar.MINUTE, 0); - this.set(Calendar.SECOND, 0); - this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.MONTH, 1); - break; - default: - throw new IllegalStateException("Unknown periodicity type."); - } - return getTime(); - } -} diff --git a/java/src/org/apache/log4j/DefaultCategoryFactory.java b/java/src/org/apache/log4j/DefaultCategoryFactory.java deleted file mode 100644 index c7bb0c4..0000000 --- a/java/src/org/apache/log4j/DefaultCategoryFactory.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.spi.LoggerFactory; - -class DefaultCategoryFactory implements LoggerFactory { - - DefaultCategoryFactory() { - } - - public - Logger makeNewLoggerInstance(String name) { - return new Logger(name); - } -} diff --git a/java/src/org/apache/log4j/DefaultThrowableRenderer.java b/java/src/org/apache/log4j/DefaultThrowableRenderer.java deleted file mode 100644 index 29bfe06..0000000 --- a/java/src/org/apache/log4j/DefaultThrowableRenderer.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j; - -import org.apache.log4j.spi.ThrowableRenderer; - -import java.io.StringWriter; -import java.io.PrintWriter; -import java.io.LineNumberReader; -import java.io.StringReader; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.util.ArrayList; - -/** - * Default implementation of ThrowableRenderer using - * Throwable.printStackTrace. - * - * @since 1.2.16 - */ -public final class DefaultThrowableRenderer implements ThrowableRenderer { - /** - * Construct new instance. - */ - public DefaultThrowableRenderer() { - - } - - - /** - * {@inheritDoc} - */ - public String[] doRender(final Throwable throwable) { - return render(throwable); - } - - /** - * Render throwable using Throwable.printStackTrace. - * @param throwable throwable, may not be null. - * @return string representation. - */ - public static String[] render(final Throwable throwable) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - try { - throwable.printStackTrace(pw); - } catch(RuntimeException ex) { - } - pw.flush(); - LineNumberReader reader = new LineNumberReader( - new StringReader(sw.toString())); - ArrayList lines = new ArrayList(); - try { - String line = reader.readLine(); - while(line != null) { - lines.add(line); - line = reader.readLine(); - } - } catch(IOException ex) { - if (ex instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - lines.add(ex.toString()); - } - String[] tempRep = new String[lines.size()]; - lines.toArray(tempRep); - return tempRep; - } -} diff --git a/java/src/org/apache/log4j/Dispatcher.java b/java/src/org/apache/log4j/Dispatcher.java deleted file mode 100644 index e879ff0..0000000 --- a/java/src/org/apache/log4j/Dispatcher.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.helpers.AppenderAttachableImpl; -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Obsolete AsyncAppender dispatcher provided for compatibility only. - * - * @deprecated Since 1.3. - */ -class Dispatcher extends Thread { - /** - * @deprecated - */ - private org.apache.log4j.helpers.BoundedFIFO bf; - private AppenderAttachableImpl aai; - private boolean interrupted = false; - AsyncAppender container; - - /** - * - * @param bf - * @param container - * @deprecated - */ - Dispatcher(org.apache.log4j.helpers.BoundedFIFO bf, AsyncAppender container) { - this.bf = bf; - this.container = container; - this.aai = container.aai; - - // It is the user's responsibility to close appenders before - // exiting. - this.setDaemon(true); - - // set the dispatcher priority to lowest possible value - this.setPriority(Thread.MIN_PRIORITY); - this.setName("Dispatcher-" + getName()); - - // set the dispatcher priority to MIN_PRIORITY plus or minus 2 - // depending on the direction of MIN to MAX_PRIORITY. - //+ (Thread.MAX_PRIORITY > Thread.MIN_PRIORITY ? 1 : -1)*2); - } - - void close() { - synchronized (bf) { - interrupted = true; - - // We have a waiting dispacther if and only if bf.length is - // zero. In that case, we need to give it a death kiss. - if (bf.length() == 0) { - bf.notify(); - } - } - } - - /** - * The dispatching strategy is to wait until there are events in the buffer - * to process. After having processed an event, we release the monitor - * (variable bf) so that new events can be placed in the buffer, instead of - * keeping the monitor and processing the remaining events in the buffer. - * - *

- * Other approaches might yield better results. - *

- */ - public void run() { - //Category cat = Category.getInstance(Dispatcher.class.getName()); - LoggingEvent event; - - while (true) { - synchronized (bf) { - if (bf.length() == 0) { - // Exit loop if interrupted but only if the the buffer is empty. - if (interrupted) { - //cat.info("Exiting."); - break; - } - - try { - //LogLog.debug("Waiting for new event to dispatch."); - bf.wait(); - } catch (InterruptedException e) { - break; - } - } - - event = bf.get(); - - if (bf.wasFull()) { - //LogLog.debug("Notifying AsyncAppender about freed space."); - bf.notify(); - } - } - - // synchronized - synchronized (container.aai) { - if ((aai != null) && (event != null)) { - aai.appendLoopOnAppenders(event); - } - } - } - - // while - // close and remove all appenders - aai.removeAllAppenders(); - } -} diff --git a/java/src/org/apache/log4j/EnhancedPatternLayout.java b/java/src/org/apache/log4j/EnhancedPatternLayout.java deleted file mode 100644 index 926053a..0000000 --- a/java/src/org/apache/log4j/EnhancedPatternLayout.java +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.helpers.OptionConverter; -import org.apache.log4j.helpers.PatternConverter; -import org.apache.log4j.pattern.BridgePatternConverter; -import org.apache.log4j.spi.LoggingEvent; - - -// Contributors: Nelson Minar -// Anders Kristensen - -/** - * This class is an enhanced version of org.apache.log4j.PatternLayout - * which was originally developed as part of the abandoned log4j 1.3 - * effort and has been available in the extras companion. - * This pattern layout should be used in preference to - * org.apache.log4j.PatternLayout except when compatibility - * where PatternLayout has been extended either through subclassing - * or alternative pattern parsers. - * - * - *

A flexible layout configurable with pattern string. The goal of this class - * is to {@link #format format} a {@link LoggingEvent} and return the results - * in a {@link StringBuffer}. The format of the result depends on the - * conversion pattern. - *

- * - *

The conversion pattern is closely related to the conversion - * pattern of the printf function in C. A conversion pattern is - * composed of literal text and format control expressions called - * conversion specifiers. - * - *

Note that you are free to insert any literal text within the - * conversion pattern. - *

- -

Each conversion specifier starts with a percent sign (%) and is - followed by optional format modifiers and a conversion - character. The conversion character specifies the type of - data, e.g. category, priority, date, thread name. The format - modifiers control such things as field width, padding, left and - right justification. The following is a simple example. - -

Let the conversion pattern be "%-5p [%t]: %m%n" and assume - that the log4j environment was set to use a EnhancedPatternLayout. Then the - statements -

-   Category root = Category.getRoot();
-   root.debug("Message 1");
-   root.warn("Message 2");
-   
- would yield the output -
-   DEBUG [main]: Message 1
-   WARN  [main]: Message 2
-   
- -

Note that there is no explicit separator between text and - conversion specifiers. The pattern parser knows when it has reached - the end of a conversion specifier when it reads a conversion - character. In the example above the conversion specifier - %-5p means the priority of the logging event should be left - justified to a width of five characters. - - The recognized conversion characters are - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Conversion CharacterEffect
cUsed to output the category of the logging event. The - category conversion specifier can be optionally followed by - NameAbbreviator pattern. - -

For example, for the category name "alpha.beta.gamma" the pattern - %c{2} will output the last two elements ("beta.gamma"), - %c{-2} will remove two elements leaving "gamma", - %c{1.} will output "a.b.gamma". - -

CUsed to output the fully qualified class name of the caller - issuing the logging request. This conversion specifier - can be optionally followed by precision specifier, that - is a decimal constant in brackets. - - Used to output the category of the logging event. The - category conversion specifier can be optionally followed by - NameAbbreviator pattern. - -

For example, for the category name "alpha.beta.gamma" the pattern - %c{2} will output the last two elements ("beta.gamma"), - %c{-2} will remove two elements leaving "gamma", - %c{1.} will output "a.b.gamma". - -

WARNING Generating the caller class information is - slow. Thus, its use should be avoided unless execution speed is - not an issue. - -

d Used to output the date of - the logging event. The date conversion specifier may be - followed by a set of braces containing a - date and time pattern strings {@link java.text.SimpleDateFormat}, - ABSOLUTE, DATE or ISO8601 - and a set of braces containing a time zone id per - {@link java.util.TimeZone#getTimeZone(String)}. - For example, %d{HH:mm:ss,SSS}, - %d{dd MMM yyyy HH:mm:ss,SSS}, - %d{DATE} or %d{HH:mm:ss}{GMT+0}. If no date format specifier is given then - ISO8601 format is assumed. -
FUsed to output the file name where the logging request was - issued. - -

WARNING Generating caller location information is - extremely slow and should be avoided unless execution speed - is not an issue. - -

lUsed to output location information of the caller which generated - the logging event. - -

The location information depends on the JVM implementation but - usually consists of the fully qualified name of the calling - method followed by the callers source the file name and line - number between parentheses. - -

The location information can be very useful. However, its - generation is extremely slow and should be avoided - unless execution speed is not an issue. - -

LUsed to output the line number from where the logging request - was issued. - -

WARNING Generating caller location information is - extremely slow and should be avoided unless execution speed - is not an issue. - -

mUsed to output the application supplied message associated with - the logging event.
MUsed to output the method name where the logging request was - issued. - -

WARNING Generating caller location information is - extremely slow and should be avoided unless execution speed - is not an issue. - -

nOutputs the platform dependent line separator character or - characters. - -

This conversion character offers practically the same - performance as using non-portable line separator strings such as - "\n", or "\r\n". Thus, it is the preferred way of specifying a - line separator. - - -

pUsed to output the priority of the logging event.
rUsed to output the number of milliseconds elapsed since the construction - of the layout until the creation of the logging event.
tUsed to output the name of the thread that generated the - logging event.
xUsed to output the NDC (nested diagnostic context) associated - with the thread that generated the logging event. -
X - -

Used to output the MDC (mapped diagnostic context) associated - with the thread that generated the logging event. The X - conversion character can be followed by the key for the - map placed between braces, as in %X{clientNumber} where - clientNumber is the key. The value in the MDC - corresponding to the key will be output. If no additional sub-option - is specified, then the entire contents of the MDC key value pair set - is output using a format {{key1,val1},{key2,val2}}

- -

See {@link MDC} class for more details. -

- -
properties -

Used to output the Properties associated - with the logging event. The properties - conversion word can be followed by the key for the - map placed between braces, as in %properties{application} where - application is the key. The value in the Properties bundle - corresponding to the key will be output. If no additional sub-option - is specified, then the entire contents of the Properties key value pair set - is output using a format {{key1,val1},{key2,val2}}

-
throwable -

Used to output the Throwable trace that has been bound to the LoggingEvent, by - default this will output the full trace as one would normally - find by a call to Throwable.printStackTrace(). - %throwable{short} or %throwable{1} will output the first line of - stack trace. throwable{none} or throwable{0} will suppress - the stack trace. %throwable{n} will output n lines of stack trace - if a positive integer or omit the last -n lines if a negative integer. - If no %throwable pattern is specified, the appender will take - responsibility to output the stack trace as it sees fit.

-
%The sequence %% outputs a single percent sign. -
- -

By default the relevant information is output as is. However, - with the aid of format modifiers it is possible to change the - minimum field width, the maximum field width and justification. - -

The optional format modifier is placed between the percent sign - and the conversion character. - -

The first optional format modifier is the left justification - flag which is just the minus (-) character. Then comes the - optional minimum field width modifier. This is a decimal - constant that represents the minimum number of characters to - output. If the data item requires fewer characters, it is padded on - either the left or the right until the minimum width is - reached. The default is to pad on the left (right justify) but you - can specify right padding with the left justification flag. The - padding character is space. If the data item is larger than the - minimum field width, the field is expanded to accommodate the - data. The value is never truncated. - -

This behavior can be changed using the maximum field - width modifier which is designated by a period followed by a - decimal constant. If the data item is longer than the maximum - field, then the extra characters are removed from the - beginning of the data item and not from the end. For - example, it the maximum field width is eight and the data item is - ten characters long, then the first two characters of the data item - are dropped. This behavior deviates from the printf function in C - where truncation is done from the end. - -

Below are various format modifier examples for the category - conversion specifier. - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
Format modifier - left justify - minimum width - maximum width - comment - -
%20cfalse20noneLeft pad with spaces if the category name is less than 20 - characters long. - -
%-20c true 20 none Right pad with - spaces if the category name is less than 20 characters long. - -
%.30cNAnone30Truncate from the beginning if the category name is longer than 30 - characters. - -
%20.30cfalse2030Left pad with spaces if the category name is shorter than 20 - characters. However, if category name is longer than 30 characters, - then truncate from the beginning. - -
%-20.30ctrue2030Right pad with spaces if the category name is shorter than 20 - characters. However, if category name is longer than 30 characters, - then truncate from the beginning. - -
- -

Below are some examples of conversion patterns. - -

- -

%r [%t] %-5p %c %x - %m%n -

This is essentially the TTCC layout. - -

%-6r [%15.15t] %-5p %30.30c %x - %m%n - -

Similar to the TTCC layout except that the relative time is - right padded if less than 6 digits, thread name is right padded if - less than 15 characters and truncated if longer and the category - name is left padded if shorter than 30 characters and truncated if - longer. - -
- -

The above text is largely inspired from Peter A. Darnell and - Philip E. Margolis' highly recommended book "C -- a Software - Engineering Approach", ISBN 0-387-97389-3. - - @author James P. Cakalic - @author Ceki Gülcü - - - @since 1.2.16 */ -public class EnhancedPatternLayout extends Layout { - /** Default pattern string for log output. Currently set to the - string "%m%n" which just prints the application supplied - message. */ - public static final String DEFAULT_CONVERSION_PATTERN = "%m%n"; - - /** A conversion pattern equivalent to the TTCCCLayout. - Current value is %r [%t] %p %c %x - %m%n. */ - public static final String TTCC_CONVERSION_PATTERN = - "%r [%t] %p %c %x - %m%n"; - - /** - * Initial size of internal buffer, no longer used. - * @deprecated since 1.3 - */ - protected final int BUF_SIZE = 256; - - /** - * Maximum capacity of internal buffer, no longer used. - * @deprecated since 1.3 - */ - protected final int MAX_CAPACITY = 1024; - - /** - * Customized pattern conversion rules are stored under this key in the - * {@link org.apache.log4j.spi.LoggerRepository LoggerRepository} object store. - */ - public static final String PATTERN_RULE_REGISTRY = "PATTERN_RULE_REGISTRY"; - - - /** - * Initial converter for pattern. - */ - private PatternConverter head; - - /** - * Conversion pattern. - */ - private String conversionPattern; - - /** - * True if any element in pattern formats information from exceptions. - */ - private boolean handlesExceptions; - - /** - Constructs a EnhancedPatternLayout using the DEFAULT_LAYOUT_PATTERN. - - The default pattern just produces the application supplied message. - */ - public EnhancedPatternLayout() { - this(DEFAULT_CONVERSION_PATTERN); - } - - /** - * Constructs a EnhancedPatternLayout using the supplied conversion pattern. - * @param pattern conversion pattern. - */ - public EnhancedPatternLayout(final String pattern) { - this.conversionPattern = pattern; - head = createPatternParser( - (pattern == null) ? DEFAULT_CONVERSION_PATTERN : pattern).parse(); - if (head instanceof BridgePatternConverter) { - handlesExceptions = !((BridgePatternConverter) head).ignoresThrowable(); - } else { - handlesExceptions = false; - } - } - - /** - * Set the ConversionPattern option. This is the string which - * controls formatting and consists of a mix of literal content and - * conversion specifiers. - * - * @param conversionPattern conversion pattern. - */ - public void setConversionPattern(final String conversionPattern) { - this.conversionPattern = - OptionConverter.convertSpecialChars(conversionPattern); - head = createPatternParser(this.conversionPattern).parse(); - if (head instanceof BridgePatternConverter) { - handlesExceptions = !((BridgePatternConverter) head).ignoresThrowable(); - } else { - handlesExceptions = false; - } - } - - /** - * Returns the value of the ConversionPattern option. - * @return conversion pattern. - */ - public String getConversionPattern() { - return conversionPattern; - } - - - /** - Returns PatternParser used to parse the conversion string. Subclasses - may override this to return a subclass of PatternParser which recognize - custom conversion characters. - - @since 0.9.0 - */ - protected org.apache.log4j.helpers.PatternParser createPatternParser(String pattern) { - return new org.apache.log4j.pattern.BridgePatternParser(pattern); - } - - - /** - Activates the conversion pattern. Do not forget to call this method after - you change the parameters of the EnhancedPatternLayout instance. - */ - public void activateOptions() { - // nothing to do. - } - - - /** - * Formats a logging event to a writer. - * @param event logging event to be formatted. - */ - public String format(final LoggingEvent event) { - StringBuffer buf = new StringBuffer(); - for(PatternConverter c = head; - c != null; - c = c.next) { - c.format(buf, event); - } - return buf.toString(); - } - - /** - * Will return false if any of the conversion specifiers in the pattern - * handles {@link Exception Exceptions}. - * @return true if the pattern formats any information from exceptions. - */ - public boolean ignoresThrowable() { - return !handlesExceptions; - } -} diff --git a/java/src/org/apache/log4j/EnhancedThrowableRenderer.java b/java/src/org/apache/log4j/EnhancedThrowableRenderer.java deleted file mode 100644 index c5b8d7b..0000000 --- a/java/src/org/apache/log4j/EnhancedThrowableRenderer.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j; - -import org.apache.log4j.spi.ThrowableRenderer; - -import java.io.File; -import java.lang.reflect.Method; -import java.net.URL; -import java.security.CodeSource; -import java.util.HashMap; -import java.util.Map; - -/** - * Enhanced implementation of ThrowableRenderer. Uses Throwable.getStackTrace - * if running on JDK 1.4 or later and delegates to DefaultThrowableRenderer.render - * on earlier virtual machines. - * - * @since 1.2.16 - */ -public final class EnhancedThrowableRenderer implements ThrowableRenderer { - /** - * Throwable.getStackTrace() method. - */ - private Method getStackTraceMethod; - /** - * StackTraceElement.getClassName() method. - */ - private Method getClassNameMethod; - - - /** - * Construct new instance. - */ - public EnhancedThrowableRenderer() { - try { - Class[] noArgs = null; - getStackTraceMethod = Throwable.class.getMethod("getStackTrace", noArgs); - Class ste = Class.forName("java.lang.StackTraceElement"); - getClassNameMethod = ste.getMethod("getClassName", noArgs); - } catch(Exception ex) { - } - } - - /** - * {@inheritDoc} - */ - public String[] doRender(final Throwable throwable) { - if (getStackTraceMethod != null) { - try { - Object[] noArgs = null; - Object[] elements = (Object[]) getStackTraceMethod.invoke(throwable, noArgs); - String[] lines = new String[elements.length + 1]; - lines[0] = throwable.toString(); - Map classMap = new HashMap(); - for(int i = 0; i < elements.length; i++) { - lines[i+1] = formatElement(elements[i], classMap); - } - return lines; - } catch(Exception ex) { - } - } - return DefaultThrowableRenderer.render(throwable); - } - - /** - * Format one element from stack trace. - * @param element element, may not be null. - * @param classMap map of class name to location. - * @return string representation of element. - */ - private String formatElement(final Object element, final Map classMap) { - StringBuffer buf = new StringBuffer("\tat "); - buf.append(element); - try { - String className = getClassNameMethod.invoke(element, (Object[]) null).toString(); - Object classDetails = classMap.get(className); - if (classDetails != null) { - buf.append(classDetails); - } else { - Class cls = findClass(className); - int detailStart = buf.length(); - buf.append('['); - try { - CodeSource source = cls.getProtectionDomain().getCodeSource(); - if (source != null) { - URL locationURL = source.getLocation(); - if (locationURL != null) { - // - // if a file: URL - // - if ("file".equals(locationURL.getProtocol())) { - String path = locationURL.getPath(); - if (path != null) { - // - // find the last file separator character - // - int lastSlash = path.lastIndexOf('/'); - int lastBack = path.lastIndexOf(File.separatorChar); - if (lastBack > lastSlash) { - lastSlash = lastBack; - } - // - // if no separator or ends with separator (a directory) - // then output the URL, otherwise just the file name. - // - if (lastSlash <= 0 || lastSlash == path.length() - 1) { - buf.append(locationURL); - } else { - buf.append(path.substring(lastSlash + 1)); - } - } - } else { - buf.append(locationURL); - } - } - } - } catch(SecurityException ex) { - } - buf.append(':'); - Package pkg = cls.getPackage(); - if (pkg != null) { - String implVersion = pkg.getImplementationVersion(); - if (implVersion != null) { - buf.append(implVersion); - } - } - buf.append(']'); - classMap.put(className, buf.substring(detailStart)); - } - } catch(Exception ex) { - } - return buf.toString(); - } - - /** - * Find class given class name. - * @param className class name, may not be null. - * @return class, will not be null. - * @throws ClassNotFoundException thrown if class can not be found. - */ - private Class findClass(final String className) throws ClassNotFoundException { - try { - return Thread.currentThread().getContextClassLoader().loadClass(className); - } catch (ClassNotFoundException e) { - try { - return Class.forName(className); - } catch (ClassNotFoundException e1) { - return getClass().getClassLoader().loadClass(className); - } - } - } - -} diff --git a/java/src/org/apache/log4j/FileAppender.java b/java/src/org/apache/log4j/FileAppender.java deleted file mode 100644 index 0728695..0000000 --- a/java/src/org/apache/log4j/FileAppender.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.io.Writer; - -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.helpers.QuietWriter; -import org.apache.log4j.spi.ErrorCode; - -// Contibutors: Jens Uwe Pipka -// Ben Sandee - -/** - * FileAppender appends log events to a file. - * - *

Support for java.io.Writer and console appending - * has been deprecated and then removed. See the replacement - * solutions: {@link WriterAppender} and {@link ConsoleAppender}. - * - * @author Ceki Gülcü - * */ -public class FileAppender extends WriterAppender { - - /** Controls file truncatation. The default value for this variable - * is true, meaning that by default a - * FileAppender will append to an existing file and not - * truncate it. - * - *

This option is meaningful only if the FileAppender opens the - * file. - */ - protected boolean fileAppend = true; - - /** - The name of the log file. */ - protected String fileName = null; - - /** - Do we do bufferedIO? */ - protected boolean bufferedIO = false; - - /** - * Determines the size of IO buffer be. Default is 8K. - */ - protected int bufferSize = 8*1024; - - - /** - The default constructor does not do anything. - */ - public - FileAppender() { - } - - /** - Instantiate a FileAppender and open the file - designated by filename. The opened filename will - become the output destination for this appender. - -

If the append parameter is true, the file will be - appended to. Otherwise, the file designated by - filename will be truncated before being opened. - -

If the bufferedIO parameter is true, - then buffered IO will be used to write to the output file. - - */ - public - FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO, - int bufferSize) throws IOException { - this.layout = layout; - this.setFile(filename, append, bufferedIO, bufferSize); - } - - /** - Instantiate a FileAppender and open the file designated by - filename. The opened filename will become the output - destination for this appender. - -

If the append parameter is true, the file will be - appended to. Otherwise, the file designated by - filename will be truncated before being opened. - */ - public - FileAppender(Layout layout, String filename, boolean append) - throws IOException { - this.layout = layout; - this.setFile(filename, append, false, bufferSize); - } - - /** - Instantiate a FileAppender and open the file designated by - filename. The opened filename will become the output - destination for this appender. - -

The file will be appended to. */ - public - FileAppender(Layout layout, String filename) throws IOException { - this(layout, filename, true); - } - - /** - The File property takes a string value which should be the - name of the file to append to. - -

Note that the special values - "System.out" or "System.err" are no longer honored. - -

Note: Actual opening of the file is made when {@link - #activateOptions} is called, not when the options are set. */ - public void setFile(String file) { - // Trim spaces from both ends. The users probably does not want - // trailing spaces in file names. - String val = file.trim(); - fileName = val; - } - - /** - Returns the value of the Append option. - */ - public - boolean getAppend() { - return fileAppend; - } - - - /** Returns the value of the File option. */ - public - String getFile() { - return fileName; - } - - /** - If the value of File is not null, then {@link - #setFile} is called with the values of File and - Append properties. - - @since 0.8.1 */ - public - void activateOptions() { - if(fileName != null) { - try { - setFile(fileName, fileAppend, bufferedIO, bufferSize); - } - catch(java.io.IOException e) { - errorHandler.error("setFile("+fileName+","+fileAppend+") call failed.", - e, ErrorCode.FILE_OPEN_FAILURE); - } - } else { - //LogLog.error("File option not set for appender ["+name+"]."); - LogLog.warn("File option not set for appender ["+name+"]."); - LogLog.warn("Are you using FileAppender instead of ConsoleAppender?"); - } - } - - /** - Closes the previously opened file. - */ - protected - void closeFile() { - if(this.qw != null) { - try { - this.qw.close(); - } - catch(java.io.IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - // Exceptionally, it does not make sense to delegate to an - // ErrorHandler. Since a closed appender is basically dead. - LogLog.error("Could not close " + qw, e); - } - } - } - - /** - Get the value of the BufferedIO option. - -

BufferedIO will significatnly increase performance on heavily - loaded systems. - - */ - public - boolean getBufferedIO() { - return this.bufferedIO; - } - - - /** - Get the size of the IO buffer. - */ - public - int getBufferSize() { - return this.bufferSize; - } - - - - /** - The Append option takes a boolean value. It is set to - true by default. If true, then File - will be opened in append mode by {@link #setFile setFile} (see - above). Otherwise, {@link #setFile setFile} will open - File in truncate mode. - -

Note: Actual opening of the file is made when {@link - #activateOptions} is called, not when the options are set. - */ - public - void setAppend(boolean flag) { - fileAppend = flag; - } - - /** - The BufferedIO option takes a boolean value. It is set to - false by default. If true, then File - will be opened and the resulting {@link java.io.Writer} wrapped - around a {@link BufferedWriter}. - - BufferedIO will significatnly increase performance on heavily - loaded systems. - - */ - public - void setBufferedIO(boolean bufferedIO) { - this.bufferedIO = bufferedIO; - if(bufferedIO) { - immediateFlush = false; - } - } - - - /** - Set the size of the IO buffer. - */ - public - void setBufferSize(int bufferSize) { - this.bufferSize = bufferSize; - } - - /** -

Sets and opens the file where the log output will - go. The specified file must be writable. - -

If there was already an opened file, then the previous file - is closed first. - -

Do not use this method directly. To configure a FileAppender - or one of its subclasses, set its properties one by one and then - call activateOptions. - - @param fileName The path to the log file. - @param append If true will append to fileName. Otherwise will - truncate fileName. */ - public - synchronized - void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) - throws IOException { - LogLog.debug("setFile called: "+fileName+", "+append); - - // It does not make sense to have immediate flush and bufferedIO. - if(bufferedIO) { - setImmediateFlush(false); - } - - reset(); - FileOutputStream ostream = null; - try { - // - // attempt to create file - // - ostream = new FileOutputStream(fileName, append); - } catch(FileNotFoundException ex) { - // - // if parent directory does not exist then - // attempt to create it and try to create file - // see bug 9150 - // - String parentName = new File(fileName).getParent(); - if (parentName != null) { - File parentDir = new File(parentName); - if(!parentDir.exists() && parentDir.mkdirs()) { - ostream = new FileOutputStream(fileName, append); - } else { - throw ex; - } - } else { - throw ex; - } - } - Writer fw = createWriter(ostream); - if(bufferedIO) { - fw = new BufferedWriter(fw, bufferSize); - } - this.setQWForFiles(fw); - this.fileName = fileName; - this.fileAppend = append; - this.bufferedIO = bufferedIO; - this.bufferSize = bufferSize; - writeHeader(); - LogLog.debug("setFile ended"); - } - - - /** - Sets the quiet writer being used. - - This method is overriden by {@link RollingFileAppender}. - */ - protected - void setQWForFiles(Writer writer) { - this.qw = new QuietWriter(writer, errorHandler); - } - - - /** - Close any previously opened file and call the parent's - reset. */ - protected - void reset() { - closeFile(); - this.fileName = null; - super.reset(); - } -} - diff --git a/java/src/org/apache/log4j/HTMLLayout.java b/java/src/org/apache/log4j/HTMLLayout.java deleted file mode 100644 index f7020cf..0000000 --- a/java/src/org/apache/log4j/HTMLLayout.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.helpers.Transform; - -/** - * This layout outputs events in a HTML table. - * - * Appenders using this layout should have their encoding - * set to UTF-8 or UTF-16, otherwise events containing - * non ASCII characters could result in corrupted - * log files. - * - * @author Ceki Gülcü - */ -public class HTMLLayout extends Layout { - - protected final int BUF_SIZE = 256; - protected final int MAX_CAPACITY = 1024; - - static String TRACE_PREFIX = "
    "; - - // output buffer appended to when format() is invoked - private StringBuffer sbuf = new StringBuffer(BUF_SIZE); - - /** - A string constant used in naming the option for setting the the - location information flag. Current value of this string - constant is LocationInfo. - -

Note that all option keys are case sensitive. - - @deprecated Options are now handled using the JavaBeans paradigm. - This constant is not longer needed and will be removed in the - near term. - - */ - public static final String LOCATION_INFO_OPTION = "LocationInfo"; - - /** - A string constant used in naming the option for setting the the - HTML document title. Current value of this string - constant is Title. - */ - public static final String TITLE_OPTION = "Title"; - - // Print no location info by default - boolean locationInfo = false; - - String title = "Log4J Log Messages"; - - /** - The LocationInfo option takes a boolean value. By - default, it is set to false which means there will be no location - information output by this layout. If the the option is set to - true, then the file name and line number of the statement - at the origin of the log statement will be output. - -

If you are embedding this layout within an {@link - org.apache.log4j.net.SMTPAppender} then make sure to set the - LocationInfo option of that appender as well. - */ - public - void setLocationInfo(boolean flag) { - locationInfo = flag; - } - - /** - Returns the current value of the LocationInfo option. - */ - public - boolean getLocationInfo() { - return locationInfo; - } - - /** - The Title option takes a String value. This option sets the - document title of the generated HTML document. - -

Defaults to 'Log4J Log Messages'. - */ - public - void setTitle(String title) { - this.title = title; - } - - /** - Returns the current value of the Title option. - */ - public - String getTitle() { - return title; - } - - /** - Returns the content type output by this layout, i.e "text/html". - */ - public - String getContentType() { - return "text/html"; - } - - /** - No options to activate. - */ - public - void activateOptions() { - } - - public - String format(LoggingEvent event) { - - if(sbuf.capacity() > MAX_CAPACITY) { - sbuf = new StringBuffer(BUF_SIZE); - } else { - sbuf.setLength(0); - } - - sbuf.append(Layout.LINE_SEP + "" + Layout.LINE_SEP); - - sbuf.append(""); - sbuf.append(event.timeStamp - LoggingEvent.getStartTime()); - sbuf.append("" + Layout.LINE_SEP); - - String escapedThread = Transform.escapeTags(event.getThreadName()); - sbuf.append(""); - sbuf.append(escapedThread); - sbuf.append("" + Layout.LINE_SEP); - - sbuf.append(""); - if (event.getLevel().equals(Level.DEBUG)) { - sbuf.append(""); - sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel()))); - sbuf.append(""); - } - else if(event.getLevel().isGreaterOrEqual(Level.WARN)) { - sbuf.append(""); - sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel()))); - sbuf.append(""); - } else { - sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel()))); - } - sbuf.append("" + Layout.LINE_SEP); - - String escapedLogger = Transform.escapeTags(event.getLoggerName()); - sbuf.append(""); - sbuf.append(escapedLogger); - sbuf.append("" + Layout.LINE_SEP); - - if(locationInfo) { - LocationInfo locInfo = event.getLocationInformation(); - sbuf.append(""); - sbuf.append(Transform.escapeTags(locInfo.getFileName())); - sbuf.append(':'); - sbuf.append(locInfo.getLineNumber()); - sbuf.append("" + Layout.LINE_SEP); - } - - sbuf.append(""); - sbuf.append(Transform.escapeTags(event.getRenderedMessage())); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - - if (event.getNDC() != null) { - sbuf.append(""); - sbuf.append("NDC: " + Transform.escapeTags(event.getNDC())); - sbuf.append("" + Layout.LINE_SEP); - } - - String[] s = event.getThrowableStrRep(); - if(s != null) { - sbuf.append(""); - appendThrowableAsHTML(s, sbuf); - sbuf.append("" + Layout.LINE_SEP); - } - - return sbuf.toString(); - } - - void appendThrowableAsHTML(String[] s, StringBuffer sbuf) { - if(s != null) { - int len = s.length; - if(len == 0) - return; - sbuf.append(Transform.escapeTags(s[0])); - sbuf.append(Layout.LINE_SEP); - for(int i = 1; i < len; i++) { - sbuf.append(TRACE_PREFIX); - sbuf.append(Transform.escapeTags(s[i])); - sbuf.append(Layout.LINE_SEP); - } - } - } - - /** - Returns appropriate HTML headers. - */ - public - String getHeader() { - StringBuffer sbuf = new StringBuffer(); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + title + "" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("


" + Layout.LINE_SEP); - sbuf.append("Log session start time " + new java.util.Date() + "
" + Layout.LINE_SEP); - sbuf.append("
" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - if(locationInfo) { - sbuf.append("" + Layout.LINE_SEP); - } - sbuf.append("" + Layout.LINE_SEP); - sbuf.append("" + Layout.LINE_SEP); - return sbuf.toString(); - } - - /** - Returns the appropriate HTML footers. - */ - public - String getFooter() { - StringBuffer sbuf = new StringBuffer(); - sbuf.append("
TimeThreadLevelCategoryFile:LineMessage
" + Layout.LINE_SEP); - sbuf.append("
" + Layout.LINE_SEP); - sbuf.append(""); - return sbuf.toString(); - } - - /** - The HTML layout handles the throwable contained in logging - events. Hence, this method return false. */ - public - boolean ignoresThrowable() { - return false; - } -} diff --git a/java/src/org/apache/log4j/Hierarchy.java b/java/src/org/apache/log4j/Hierarchy.java deleted file mode 100644 index 5b712e0..0000000 --- a/java/src/org/apache/log4j/Hierarchy.java +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// WARNING This class MUST not have references to the Category or -// WARNING RootCategory classes in its static initiliazation neither -// WARNING directly nor indirectly. - -// Contributors: -// Luke Blanshard -// Mario Schomburg - IBM Global Services/Germany -// Anders Kristensen -// Igor Poteryaev - -package org.apache.log4j; - - -import java.util.Hashtable; -import java.util.Enumeration; -import java.util.Vector; - -import org.apache.log4j.spi.LoggerFactory; -import org.apache.log4j.spi.HierarchyEventListener; -import org.apache.log4j.spi.LoggerRepository; -import org.apache.log4j.spi.RendererSupport; -import org.apache.log4j.or.RendererMap; -import org.apache.log4j.or.ObjectRenderer; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.spi.ThrowableRendererSupport; -import org.apache.log4j.spi.ThrowableRenderer; - -/** - This class is specialized in retrieving loggers by name and also - maintaining the logger hierarchy. - -

The casual user does not have to deal with this class - directly. - -

The structure of the logger hierarchy is maintained by the - {@link #getLogger} method. The hierarchy is such that children link - to their parent but parents do not have any pointers to their - children. Moreover, loggers can be instantiated in any order, in - particular descendant before ancestor. - -

In case a descendant is created before a particular ancestor, - then it creates a provision node for the ancestor and adds itself - to the provision node. Other descendants of the same ancestor add - themselves to the previously created provision node. - - @author Ceki Gülcü - -*/ -public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport { - - private LoggerFactory defaultFactory; - private Vector listeners; - - Hashtable ht; - Logger root; - RendererMap rendererMap; - - int thresholdInt; - Level threshold; - - boolean emittedNoAppenderWarning = false; - boolean emittedNoResourceBundleWarning = false; - - private ThrowableRenderer throwableRenderer = null; - - /** - Create a new logger hierarchy. - - @param root The root of the new hierarchy. - - */ - public - Hierarchy(Logger root) { - ht = new Hashtable(); - listeners = new Vector(1); - this.root = root; - // Enable all level levels by default. - setThreshold(Level.ALL); - this.root.setHierarchy(this); - rendererMap = new RendererMap(); - defaultFactory = new DefaultCategoryFactory(); - } - - /** - Add an object renderer for a specific class. - */ - public - void addRenderer(Class classToRender, ObjectRenderer or) { - rendererMap.put(classToRender, or); - } - - public - void addHierarchyEventListener(HierarchyEventListener listener) { - if(listeners.contains(listener)) { - LogLog.warn("Ignoring attempt to add an existent listener."); - } else { - listeners.addElement(listener); - } - } - - /** - This call will clear all logger definitions from the internal - hashtable. Invoking this method will irrevocably mess up the - logger hierarchy. - -

You should really know what you are doing before - invoking this method. - - @since 0.9.0 */ - public - void clear() { - //System.out.println("\n\nAbout to clear internal hash table."); - ht.clear(); - } - - public - void emitNoAppenderWarning(Category cat) { - // No appenders in hierarchy, warn user only once. - if(!this.emittedNoAppenderWarning) { - LogLog.warn("No appenders could be found for logger (" + - cat.getName() + ")."); - LogLog.warn("Please initialize the log4j system properly."); - LogLog.warn("See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info."); - this.emittedNoAppenderWarning = true; - } - } - - /** - Check if the named logger exists in the hierarchy. If so return - its reference, otherwise returns null. - - @param name The name of the logger to search for. - - */ - public - Logger exists(String name) { - Object o = ht.get(new CategoryKey(name)); - if(o instanceof Logger) { - return (Logger) o; - } else { - return null; - } - } - - /** - The string form of {@link #setThreshold(Level)}. - */ - public - void setThreshold(String levelStr) { - Level l = (Level) Level.toLevel(levelStr, null); - if(l != null) { - setThreshold(l); - } else { - LogLog.warn("Could not convert ["+levelStr+"] to Level."); - } - } - - - /** - Enable logging for logging requests with level l or - higher. By default all levels are enabled. - - @param l The minimum level for which logging requests are sent to - their appenders. */ - public - void setThreshold(Level l) { - if(l != null) { - thresholdInt = l.level; - threshold = l; - } - } - - public - void fireAddAppenderEvent(Category logger, Appender appender) { - if(listeners != null) { - int size = listeners.size(); - HierarchyEventListener listener; - for(int i = 0; i < size; i++) { - listener = (HierarchyEventListener) listeners.elementAt(i); - listener.addAppenderEvent(logger, appender); - } - } - } - - void fireRemoveAppenderEvent(Category logger, Appender appender) { - if(listeners != null) { - int size = listeners.size(); - HierarchyEventListener listener; - for(int i = 0; i < size; i++) { - listener = (HierarchyEventListener) listeners.elementAt(i); - listener.removeAppenderEvent(logger, appender); - } - } - } - - /** - Returns a {@link Level} representation of the enable - state. - - @since 1.2 */ - public - Level getThreshold() { - return threshold; - } - - /** - Returns an integer representation of the this repository's - threshold. - - @since 1.2 */ - //public - //int getThresholdInt() { - // return thresholdInt; - //} - - - /** - Return a new logger instance named as the first parameter using - the default factory. - -

If a logger of that name already exists, then it will be - returned. Otherwise, a new logger will be instantiated and - then linked with its existing ancestors as well as children. - - @param name The name of the logger to retrieve. - - */ - public - Logger getLogger(String name) { - return getLogger(name, defaultFactory); - } - - /** - Return a new logger instance named as the first parameter using - factory. - -

If a logger of that name already exists, then it will be - returned. Otherwise, a new logger will be instantiated by the - factory parameter and linked with its existing - ancestors as well as children. - - @param name The name of the logger to retrieve. - @param factory The factory that will make the new logger instance. - - */ - public - Logger getLogger(String name, LoggerFactory factory) { - //System.out.println("getInstance("+name+") called."); - CategoryKey key = new CategoryKey(name); - // Synchronize to prevent write conflicts. Read conflicts (in - // getChainedLevel method) are possible only if variable - // assignments are non-atomic. - Logger logger; - - synchronized(ht) { - Object o = ht.get(key); - if(o == null) { - logger = factory.makeNewLoggerInstance(name); - logger.setHierarchy(this); - ht.put(key, logger); - updateParents(logger); - return logger; - } else if(o instanceof Logger) { - return (Logger) o; - } else if (o instanceof ProvisionNode) { - //System.out.println("("+name+") ht.get(this) returned ProvisionNode"); - logger = factory.makeNewLoggerInstance(name); - logger.setHierarchy(this); - ht.put(key, logger); - updateChildren((ProvisionNode) o, logger); - updateParents(logger); - return logger; - } - else { - // It should be impossible to arrive here - return null; // but let's keep the compiler happy. - } - } - } - - /** - Returns all the currently defined categories in this hierarchy as - an {@link java.util.Enumeration Enumeration}. - -

The root logger is not included in the returned - {@link Enumeration}. */ - public - Enumeration getCurrentLoggers() { - // The accumlation in v is necessary because not all elements in - // ht are Logger objects as there might be some ProvisionNodes - // as well. - Vector v = new Vector(ht.size()); - - Enumeration elems = ht.elements(); - while(elems.hasMoreElements()) { - Object o = elems.nextElement(); - if(o instanceof Logger) { - v.addElement(o); - } - } - return v.elements(); - } - - /** - @deprecated Please use {@link #getCurrentLoggers} instead. - */ - public - Enumeration getCurrentCategories() { - return getCurrentLoggers(); - } - - - /** - Get the renderer map for this hierarchy. - */ - public - RendererMap getRendererMap() { - return rendererMap; - } - - - /** - Get the root of this hierarchy. - - @since 0.9.0 - */ - public - Logger getRootLogger() { - return root; - } - - /** - This method will return true if this repository is - disabled for level object passed as parameter and - false otherwise. See also the {@link - #setThreshold(Level) threshold} emthod. */ - public - boolean isDisabled(int level) { - return thresholdInt > level; - } - - /** - @deprecated Deprecated with no replacement. - */ - public - void overrideAsNeeded(String override) { - LogLog.warn("The Hiearchy.overrideAsNeeded method has been deprecated."); - } - - /** - Reset all values contained in this hierarchy instance to their - default. This removes all appenders from all categories, sets - the level of all non-root categories to null, - sets their additivity flag to true and sets the level - of the root logger to {@link Level#DEBUG DEBUG}. Moreover, - message disabling is set its default "off" value. - -

Existing categories are not removed. They are just reset. - -

This method should be used sparingly and with care as it will - block all logging until it is completed.

- - @since 0.8.5 */ - public - void resetConfiguration() { - - getRootLogger().setLevel((Level) Level.DEBUG); - root.setResourceBundle(null); - setThreshold(Level.ALL); - - // the synchronization is needed to prevent JDK 1.2.x hashtable - // surprises - synchronized(ht) { - shutdown(); // nested locks are OK - - Enumeration cats = getCurrentLoggers(); - while(cats.hasMoreElements()) { - Logger c = (Logger) cats.nextElement(); - c.setLevel(null); - c.setAdditivity(true); - c.setResourceBundle(null); - } - } - rendererMap.clear(); - throwableRenderer = null; - } - - /** - Does nothing. - - @deprecated Deprecated with no replacement. - */ - public - void setDisableOverride(String override) { - LogLog.warn("The Hiearchy.setDisableOverride method has been deprecated."); - } - - - - /** - Used by subclasses to add a renderer to the hierarchy passed as parameter. - */ - public - void setRenderer(Class renderedClass, ObjectRenderer renderer) { - rendererMap.put(renderedClass, renderer); - } - - /** - * {@inheritDoc} - */ - public void setThrowableRenderer(final ThrowableRenderer renderer) { - throwableRenderer = renderer; - } - - /** - * {@inheritDoc} - */ - public ThrowableRenderer getThrowableRenderer() { - return throwableRenderer; - } - - - /** - Shutting down a hierarchy will safely close and remove - all appenders in all categories including the root logger. - -

Some appenders such as {@link org.apache.log4j.net.SocketAppender} - and {@link AsyncAppender} need to be closed before the - application exists. Otherwise, pending logging events might be - lost. - -

The shutdown method is careful to close nested - appenders before closing regular appenders. This is allows - configurations where a regular appender is attached to a logger - and again to a nested appender. - - - @since 1.0 */ - public - void shutdown() { - Logger root = getRootLogger(); - - // begin by closing nested appenders - root.closeNestedAppenders(); - - synchronized(ht) { - Enumeration cats = this.getCurrentLoggers(); - while(cats.hasMoreElements()) { - Logger c = (Logger) cats.nextElement(); - c.closeNestedAppenders(); - } - - // then, remove all appenders - root.removeAllAppenders(); - cats = this.getCurrentLoggers(); - while(cats.hasMoreElements()) { - Logger c = (Logger) cats.nextElement(); - c.removeAllAppenders(); - } - } - } - - - /** - This method loops through all the *potential* parents of - 'cat'. There 3 possible cases: - - 1) No entry for the potential parent of 'cat' exists - - We create a ProvisionNode for this potential parent and insert - 'cat' in that provision node. - - 2) There entry is of type Logger for the potential parent. - - The entry is 'cat's nearest existing parent. We update cat's - parent field with this entry. We also break from the loop - because updating our parent's parent is our parent's - responsibility. - - 3) There entry is of type ProvisionNode for this potential parent. - - We add 'cat' to the list of children for this potential parent. - */ - final - private - void updateParents(Logger cat) { - String name = cat.name; - int length = name.length(); - boolean parentFound = false; - - //System.out.println("UpdateParents called for " + name); - - // if name = "w.x.y.z", loop thourgh "w.x.y", "w.x" and "w", but not "w.x.y.z" - for(int i = name.lastIndexOf('.', length-1); i >= 0; - i = name.lastIndexOf('.', i-1)) { - String substr = name.substring(0, i); - - //System.out.println("Updating parent : " + substr); - CategoryKey key = new CategoryKey(substr); // simple constructor - Object o = ht.get(key); - // Create a provision node for a future parent. - if(o == null) { - //System.out.println("No parent "+substr+" found. Creating ProvisionNode."); - ProvisionNode pn = new ProvisionNode(cat); - ht.put(key, pn); - } else if(o instanceof Category) { - parentFound = true; - cat.parent = (Category) o; - //System.out.println("Linking " + cat.name + " -> " + ((Category) o).name); - break; // no need to update the ancestors of the closest ancestor - } else if(o instanceof ProvisionNode) { - ((ProvisionNode) o).addElement(cat); - } else { - Exception e = new IllegalStateException("unexpected object type " + - o.getClass() + " in ht."); - e.printStackTrace(); - } - } - // If we could not find any existing parents, then link with root. - if(!parentFound) - cat.parent = root; - } - - /** - We update the links for all the children that placed themselves - in the provision node 'pn'. The second argument 'cat' is a - reference for the newly created Logger, parent of all the - children in 'pn' - - We loop on all the children 'c' in 'pn': - - If the child 'c' has been already linked to a child of - 'cat' then there is no need to update 'c'. - - Otherwise, we set cat's parent field to c's parent and set - c's parent field to cat. - - */ - final - private - void updateChildren(ProvisionNode pn, Logger logger) { - //System.out.println("updateChildren called for " + logger.name); - final int last = pn.size(); - - for(int i = 0; i < last; i++) { - Logger l = (Logger) pn.elementAt(i); - //System.out.println("Updating child " +p.name); - - // Unless this child already points to a correct (lower) parent, - // make cat.parent point to l.parent and l.parent to cat. - if(!l.parent.name.startsWith(logger.name)) { - logger.parent = l.parent; - l.parent = logger; - } - } - } - -} - - diff --git a/java/src/org/apache/log4j/Layout.java b/java/src/org/apache/log4j/Layout.java deleted file mode 100644 index 63015aa..0000000 --- a/java/src/org/apache/log4j/Layout.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.spi.OptionHandler; -import org.apache.log4j.spi.LoggingEvent; - -/** - Extend this abstract class to create your own log layout format. - - @author Ceki Gülcü - -*/ - -public abstract class Layout implements OptionHandler { - - // Note that the line.separator property can be looked up even by - // applets. - public final static String LINE_SEP = System.getProperty("line.separator"); - public final static int LINE_SEP_LEN = LINE_SEP.length(); - - - /** - Implement this method to create your own layout format. - */ - abstract - public - String format(LoggingEvent event); - - /** - Returns the content type output by this layout. The base class - returns "text/plain". - */ - public - String getContentType() { - return "text/plain"; - } - - /** - Returns the header for the layout format. The base class returns - null. */ - public - String getHeader() { - return null; - } - - /** - Returns the footer for the layout format. The base class returns - null. */ - public - String getFooter() { - return null; - } - - - - /** - If the layout handles the throwable object contained within - {@link LoggingEvent}, then the layout should return - false. Otherwise, if the layout ignores throwable - object, then the layout should return true. - If ignoresThrowable is true, the appender is responsible for - rendering the throwable. - -

The {@link SimpleLayout}, {@link TTCCLayout}, {@link - PatternLayout} all return true. The {@link - org.apache.log4j.xml.XMLLayout} returns false. - - @since 0.8.4 */ - abstract - public - boolean ignoresThrowable(); - -} diff --git a/java/src/org/apache/log4j/Level.java b/java/src/org/apache/log4j/Level.java deleted file mode 100644 index 5028862..0000000 --- a/java/src/org/apache/log4j/Level.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contributors: Kitching Simon -// Nicholas Wolff - -package org.apache.log4j; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamException; -import java.io.Serializable; - -/** - Defines the minimum set of levels recognized by the system, that is - OFF, FATAL, ERROR, - WARN, INFODEBUG and - ALL. - -

The Level class may be subclassed to define a larger - level set. - - @author Ceki Gülcü - - */ -public class Level extends Priority implements Serializable { - - /** - * TRACE level integer value. - * @since 1.2.12 - */ - public static final int TRACE_INT = 5000; - - /** - The OFF has the highest possible rank and is - intended to turn off logging. */ - final static public Level OFF = new Level(OFF_INT, "OFF", 0); - - /** - The FATAL level designates very severe error - events that will presumably lead the application to abort. - */ - final static public Level FATAL = new Level(FATAL_INT, "FATAL", 0); - - /** - The ERROR level designates error events that - might still allow the application to continue running. */ - final static public Level ERROR = new Level(ERROR_INT, "ERROR", 3); - - /** - The WARN level designates potentially harmful situations. - */ - final static public Level WARN = new Level(WARN_INT, "WARN", 4); - - /** - The INFO level designates informational messages - that highlight the progress of the application at coarse-grained - level. */ - final static public Level INFO = new Level(INFO_INT, "INFO", 6); - - /** - The DEBUG Level designates fine-grained - informational events that are most useful to debug an - application. */ - final static public Level DEBUG = new Level(DEBUG_INT, "DEBUG", 7); - - /** - * The TRACE Level designates finer-grained - * informational events than the DEBUGALL has the lowest possible rank and is intended to - turn on all logging. */ - final static public Level ALL = new Level(ALL_INT, "ALL", 7); - - /** - * Serialization version id. - */ - static final long serialVersionUID = 3491141966387921974L; - - /** - Instantiate a Level object. - */ - protected - Level(int level, String levelStr, int syslogEquivalent) { - super(level, levelStr, syslogEquivalent); - } - - - /** - Convert the string passed as argument to a level. If the - conversion fails, then this method returns {@link #DEBUG}. - */ - public - static - Level toLevel(String sArg) { - return (Level) toLevel(sArg, Level.DEBUG); - } - - /** - Convert an integer passed as argument to a level. If the - conversion fails, then this method returns {@link #DEBUG}. - - */ - public - static - Level toLevel(int val) { - return (Level) toLevel(val, Level.DEBUG); - } - - /** - Convert an integer passed as argument to a level. If the - conversion fails, then this method returns the specified default. - */ - public - static - Level toLevel(int val, Level defaultLevel) { - switch(val) { - case ALL_INT: return ALL; - case DEBUG_INT: return Level.DEBUG; - case INFO_INT: return Level.INFO; - case WARN_INT: return Level.WARN; - case ERROR_INT: return Level.ERROR; - case FATAL_INT: return Level.FATAL; - case OFF_INT: return OFF; - case TRACE_INT: return Level.TRACE; - default: return defaultLevel; - } - } - - /** - Convert the string passed as argument to a level. If the - conversion fails, then this method returns the value of - defaultLevel. - */ - public - static - Level toLevel(String sArg, Level defaultLevel) { - if(sArg == null) - return defaultLevel; - - String s = sArg.toUpperCase(); - - if(s.equals("ALL")) return Level.ALL; - if(s.equals("DEBUG")) return Level.DEBUG; - if(s.equals("INFO")) return Level.INFO; - if(s.equals("WARN")) return Level.WARN; - if(s.equals("ERROR")) return Level.ERROR; - if(s.equals("FATAL")) return Level.FATAL; - if(s.equals("OFF")) return Level.OFF; - if(s.equals("TRACE")) return Level.TRACE; - // - // For Turkish i problem, see bug 40937 - // - if(s.equals("\u0130NFO")) return Level.INFO; - return defaultLevel; - } - - /** - * Custom deserialization of Level. - * @param s serialization stream. - * @throws IOException if IO exception. - * @throws ClassNotFoundException if class not found. - */ - private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException { - s.defaultReadObject(); - level = s.readInt(); - syslogEquivalent = s.readInt(); - levelStr = s.readUTF(); - if (levelStr == null) { - levelStr = ""; - } - } - - /** - * Serialize level. - * @param s serialization stream. - * @throws IOException if exception during serialization. - */ - private void writeObject(final ObjectOutputStream s) throws IOException { - s.defaultWriteObject(); - s.writeInt(level); - s.writeInt(syslogEquivalent); - s.writeUTF(levelStr); - } - - /** - * Resolved deserialized level to one of the stock instances. - * May be overriden in classes derived from Level. - * @return resolved object. - * @throws ObjectStreamException if exception during resolution. - */ - private Object readResolve() throws ObjectStreamException { - // - // if the deserizalized object is exactly an instance of Level - // - if (getClass() == Level.class) { - return toLevel(level); - } - // - // extension of Level can't substitute stock item - // - return this; - } - -} diff --git a/java/src/org/apache/log4j/LogMF.java b/java/src/org/apache/log4j/LogMF.java deleted file mode 100644 index 2df99fc..0000000 --- a/java/src/org/apache/log4j/LogMF.java +++ /dev/null @@ -1,1677 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j; - -import org.apache.log4j.spi.LoggingEvent; - -import java.text.DateFormat; -import java.text.MessageFormat; -import java.text.NumberFormat; -import java.util.Date; -import java.util.ResourceBundle; -import java.util.Locale; - - -/** - * This class provides parameterized logging services - * using the pattern syntax of java.text.MessageFormat. - * Message formatting is only performed when the - * request exceeds the threshold level of the logger. - * When the pattern only contains literal text and - * default conversion patterns (that is "{0}" and similar) - * a simple fast compatible formatter is used. - * If the pattern contains more complex conversion patterns, - * formatting will be delegated to java.text.MessageFormatter - * which can be substantially slower. - * - * @see org.apache.log4j.LogSF - * @since 1.2.16 - * - */ -public final class LogMF extends LogXF { - /** - * private constructor. - * - */ - private LogMF() { - } - - /** - * Number format. - */ - private static NumberFormat numberFormat = null; - /** - * Locale at time of last number format request. - */ - private static Locale numberLocale = null; - /** - * Date format. - */ - private static DateFormat dateFormat = null; - /** - * Locale at time of last date format request. - */ - private static Locale dateLocale = null; - - /** - * Format number. - * @param n number to format, may not be null. - * @return formatted value. - */ - private static synchronized String formatNumber(final Object n) { - Locale currentLocale = Locale.getDefault(); - if (currentLocale != numberLocale || numberFormat == null) { - numberLocale = currentLocale; - numberFormat = NumberFormat.getInstance(currentLocale); - } - return numberFormat.format(n); - } - - - /** - * Format date. - * @param d date, may not be null. - * @return formatted value. - */ - private static synchronized String formatDate(final Object d) { - Locale currentLocale = Locale.getDefault(); - if (currentLocale != dateLocale || dateFormat == null) { - dateLocale = currentLocale; - dateFormat = DateFormat.getDateTimeInstance( - DateFormat.SHORT, - DateFormat.SHORT, - currentLocale); - } - return dateFormat.format(d); - } - - /** - * Format a single parameter like a "{0}" formatting specifier. - * - * @param arg0 parameter, may be null. - * @return string representation of arg0. - */ - private static String formatObject(final Object arg0) { - if (arg0 instanceof String) { - return arg0.toString(); - } else if (arg0 instanceof Double || - arg0 instanceof Float) { - return formatNumber(arg0); - } else if (arg0 instanceof Date) { - return formatDate(arg0); - } - return String.valueOf(arg0); - } - - - /** - * Determines if pattern contains only {n} format elements - * and not apostrophes. - * - * @param pattern pattern, may not be null. - * @return true if pattern only contains {n} format elements. - */ - private static boolean isSimple(final String pattern) { - if (pattern.indexOf('\'') != -1) { - return false; - } - for(int pos = pattern.indexOf('{'); - pos != -1; - pos = pattern.indexOf('{', pos + 1)) { - if (pos + 2 >= pattern.length() || - pattern.charAt(pos+2) != '}' || - pattern.charAt(pos+1) < '0' || - pattern.charAt(pos+1) > '9') { - return false; - } - } - return true; - - } - - /** - * Formats arguments using MessageFormat. - * @param pattern pattern, may be malformed or null. - * @param arguments arguments, may be null or mismatched. - * @return Message string or null - */ - private static String format(final String pattern, - final Object[] arguments) { - if (pattern == null) { - return null; - } else if(isSimple(pattern)) { - String formatted[] = new String[10]; - int prev = 0; - String retval = ""; - int pos = pattern.indexOf('{'); - while(pos >= 0) { - if(pos + 2 < pattern.length() && - pattern.charAt(pos+2) == '}' && - pattern.charAt(pos+1) >= '0' && - pattern.charAt(pos+1) <= '9') { - int index = pattern.charAt(pos+1) - '0'; - retval += pattern.substring(prev, pos); - if (formatted[index] == null) { - if (arguments == null || index >= arguments.length) { - formatted[index] = pattern.substring(pos, pos+3); - } else { - formatted[index] = formatObject(arguments[index]); - } - } - retval += formatted[index]; - prev = pos + 3; - pos = pattern.indexOf('{', prev); - } else { - pos = pattern.indexOf('{', pos + 1); - } - } - retval += pattern.substring(prev); - return retval; - } - try { - return MessageFormat.format(pattern, arguments); - } catch (IllegalArgumentException ex) { - return pattern; - } - } - - /** - * Formats a single argument using MessageFormat. - * @param pattern pattern, may be malformed or null. - * @param arguments arguments, may be null or mismatched. - * @return Message string or null - */ - private static String format(final String pattern, - final Object arg0) { - if (pattern == null) { - return null; - } else if(isSimple(pattern)) { - String formatted = null; - int prev = 0; - String retval = ""; - int pos = pattern.indexOf('{'); - while(pos >= 0) { - if(pos + 2 < pattern.length() && - pattern.charAt(pos+2) == '}' && - pattern.charAt(pos+1) >= '0' && - pattern.charAt(pos+1) <= '9') { - int index = pattern.charAt(pos+1) - '0'; - retval += pattern.substring(prev, pos); - if (index != 0) { - retval += pattern.substring(pos, pos+3); - } else { - if (formatted == null) { - formatted = formatObject(arg0); - } - retval += formatted; - } - prev = pos + 3; - pos = pattern.indexOf('{', prev); - } else { - pos = pattern.indexOf('{', pos + 1); - } - } - retval += pattern.substring(prev); - return retval; - } - try { - return MessageFormat.format(pattern, new Object[] { arg0 }); - } catch (IllegalArgumentException ex) { - return pattern; - } - } - - - /** - * Formats arguments using MessageFormat using a pattern from - * a resource bundle. - * @param resourceBundleName name of resource bundle, may be null. - * @param key key for pattern in resource bundle, may be null. - * @param arguments arguments, may be null or mismatched. - * @return Message string or null - */ - private static String format( - final String resourceBundleName, - final String key, - final Object[] arguments) { - String pattern; - if (resourceBundleName != null) { - try { - ResourceBundle bundle = - ResourceBundle.getBundle(resourceBundleName); - pattern = bundle.getString(key); - } catch (Exception ex) { - pattern = key; - } - } else { - pattern = key; - } - return format(pattern, arguments); - } - - - /** - * Fully Qualified Class Name of this class. - */ - private static final String FQCN = LogMF.class.getName(); - - /** - * Equivalent of Logger.forcedLog. - * - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param msg message, may be null. - */ - private static void forcedLog(final Logger logger, - final Level level, - final String msg) { - logger.callAppenders(new LoggingEvent(FQCN, logger, level, msg, null)); - } - - /** - * Equivalent of Logger.forcedLog. - * - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param msg message, may be null. - * @param t throwable. - */ - private static void forcedLog(final Logger logger, - final Level level, - final String msg, - final Throwable t) { - logger.callAppenders(new LoggingEvent(FQCN, logger, level, msg, t)); - } - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be - * formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at error level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void error(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.ERROR)) { - forcedLog(logger, Level.ERROR, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at fatal level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void fatal(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.FATAL)) { - forcedLog(logger, Level.FATAL, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be - * formatted and substituted. - */ - public static void trace(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, arguments), t); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void debug(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, arguments), t); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void info(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, arguments), t); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void warn(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, arguments), t); - } - } - - /** - * Log a parameterized message at error level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void error(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.ERROR)) { - forcedLog(logger, Level.ERROR, format(pattern, arguments), t); - } - } - - /** - * Log a parameterized message at fatal level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void fatal(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.FATAL)) { - forcedLog(logger, Level.FATAL, format(pattern, arguments), t); - } - } - - - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final boolean argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final char argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final byte argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final short argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final int argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final long argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final float argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final double argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final Object argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, argument)); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final Object arg0, final Object arg1) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, - format(pattern, toArray(arg0, arg1))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, - format(pattern, toArray(arg0, arg1, arg2))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - * @param arg3 a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2, - final Object arg3) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, - format(pattern, toArray(arg0, arg1, arg2, arg3))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final boolean argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final char argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final byte argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final short argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final int argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final long argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final float argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final double argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final Object argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, argument)); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final Object arg0, final Object arg1) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, - format(pattern, toArray(arg0, arg1))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, - format(pattern, toArray(arg0, arg1, arg2))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - * @param arg3 a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2, - final Object arg3) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, - format(pattern, toArray(arg0, arg1, arg2, arg3))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final boolean argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final char argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final byte argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final short argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final int argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final long argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final float argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final double argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final Object argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, argument)); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final Object arg0, final Object arg1) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, toArray(arg0, arg1))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, - toArray(arg0, arg1, arg2))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - * @param arg3 a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2, - final Object arg3) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, - toArray(arg0, arg1, arg2, arg3))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final boolean argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final char argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final byte argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final short argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final int argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final long argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final float argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final double argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final Object argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, argument)); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final Object arg0, final Object arg1) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, - format(pattern, toArray(arg0, arg1))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, - format(pattern, toArray(arg0, arg1, arg2))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - * @param arg3 a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2, - final Object arg3) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, - toArray(arg0, arg1, arg2, arg3))); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param parameters parameters to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final Object[] parameters) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, parameters)); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param parameters parameters to the log message. - */ - public static void log(final Logger logger, - final Level level, - final Throwable t, - final String pattern, - final Object[] parameters) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, parameters), t); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final Object param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(param1))); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final boolean param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final byte param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final char param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final short param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final int param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final long param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final float param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final double param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final Object arg0, final Object arg1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(arg0, arg1))); - } - } - - /** - * Log a parameterized message at specifed level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final Object arg0, final Object arg1, final Object arg2) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(arg0, arg1, arg2))); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param level level, may not be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - * @param arg3 a value to be formatted and substituted. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final Object arg0, final Object arg1, final Object arg2, - final Object arg3) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, format(pattern, - toArray(arg0, arg1, arg2, arg3))); - } - } - - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param parameters parameters to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final Object[] parameters) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, parameters)); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param t throwable, may be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param parameters parameters to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final Throwable t, - final String bundleName, - final String key, - final Object[] parameters) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, parameters), t); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final Object param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(param1))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final boolean param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final char param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final byte param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final short param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final int param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final long param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final float param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final double param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param0 Parameter to the log message. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final Object param0, - final Object param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(param0, param1))); - } - } - - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param0 Parameter to the log message. - * @param param1 Parameter to the log message. - * @param param2 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final Object param0, - final Object param1, - final Object param2) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(param0, param1, param2))); - } - } - - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param0 Parameter to the log message. - * @param param1 Parameter to the log message. - * @param param2 Parameter to the log message. - * @param param3 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final Object param0, - final Object param1, - final Object param2, - final Object param3) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, - toArray(param0, param1, param2, param3))); - } - } - -} diff --git a/java/src/org/apache/log4j/LogManager.java b/java/src/org/apache/log4j/LogManager.java deleted file mode 100644 index 5b9659c..0000000 --- a/java/src/org/apache/log4j/LogManager.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.spi.LoggerRepository; -import org.apache.log4j.spi.LoggerFactory; -import org.apache.log4j.spi.RepositorySelector; -import org.apache.log4j.spi.DefaultRepositorySelector; -import org.apache.log4j.spi.RootLogger; -import org.apache.log4j.spi.NOPLoggerRepository; -import org.apache.log4j.helpers.Loader; -import org.apache.log4j.helpers.OptionConverter; -import org.apache.log4j.helpers.LogLog; - -import java.net.URL; -import java.net.MalformedURLException; - - -import java.util.Enumeration; -import java.io.StringWriter; -import java.io.PrintWriter; - -/** - * Use the LogManager class to retreive {@link Logger} - * instances or to operate on the current {@link - * LoggerRepository}. When the LogManager class is loaded - * into memory the default initalzation procedure is inititated. The - * default intialization procedure is described in the short log4j manual. - * - * @author Ceki Gülcü */ -public class LogManager { - - /** - * @deprecated This variable is for internal use only. It will - * become package protected in future versions. - * */ - static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties"; - - static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml"; - - /** - * @deprecated This variable is for internal use only. It will - * become private in future versions. - * */ - static final public String DEFAULT_CONFIGURATION_KEY="log4j.configuration"; - - /** - * @deprecated This variable is for internal use only. It will - * become private in future versions. - * */ - static final public String CONFIGURATOR_CLASS_KEY="log4j.configuratorClass"; - - /** - * @deprecated This variable is for internal use only. It will - * become private in future versions. - */ - public static final String DEFAULT_INIT_OVERRIDE_KEY = - "log4j.defaultInitOverride"; - - - static private Object guard = null; - static private RepositorySelector repositorySelector; - - static { - // By default we use a DefaultRepositorySelector which always returns 'h'. - Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG)); - repositorySelector = new DefaultRepositorySelector(h); - - /** Search for the properties file log4j.properties in the CLASSPATH. */ - String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY, - null); - - // if there is no default init override, then get the resource - // specified by the user or the default config file. - if(override == null || "false".equalsIgnoreCase(override)) { - - String configurationOptionStr = OptionConverter.getSystemProperty( - DEFAULT_CONFIGURATION_KEY, - null); - - String configuratorClassName = OptionConverter.getSystemProperty( - CONFIGURATOR_CLASS_KEY, - null); - - URL url = null; - - // if the user has not specified the log4j.configuration - // property, we search first for the file "log4j.xml" and then - // "log4j.properties" - if(configurationOptionStr == null) { - url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE); - if(url == null) { - url = Loader.getResource(DEFAULT_CONFIGURATION_FILE); - } - } else { - try { - url = new URL(configurationOptionStr); - } catch (MalformedURLException ex) { - // so, resource is not a URL: - // attempt to get the resource from the class path - url = Loader.getResource(configurationOptionStr); - } - } - - // If we have a non-null url, then delegate the rest of the - // configuration to the OptionConverter.selectAndConfigure - // method. - if(url != null) { - LogLog.debug("Using URL ["+url+"] for automatic log4j configuration."); - try { - OptionConverter.selectAndConfigure(url, configuratorClassName, - LogManager.getLoggerRepository()); - } catch (NoClassDefFoundError e) { - LogLog.warn("Error during default initialization", e); - } - } else { - LogLog.debug("Could not find resource: ["+configurationOptionStr+"]."); - } - } else { - LogLog.debug("Default initialization of overridden by " + - DEFAULT_INIT_OVERRIDE_KEY + "property."); - } - } - - /** - Sets LoggerFactory but only if the correct - guard is passed as parameter. - -

Initally the guard is null. If the guard is - null, then invoking this method sets the logger - factory and the guard. Following invocations will throw a {@link - IllegalArgumentException}, unless the previously set - guard is passed as the second parameter. - -

This allows a high-level component to set the {@link - RepositorySelector} used by the LogManager. - -

For example, when tomcat starts it will be able to install its - own repository selector. However, if and when Tomcat is embedded - within JBoss, then JBoss will install its own repository selector - and Tomcat will use the repository selector set by its container, - JBoss. */ - static - public - void setRepositorySelector(RepositorySelector selector, Object guard) - throws IllegalArgumentException { - if((LogManager.guard != null) && (LogManager.guard != guard)) { - throw new IllegalArgumentException( - "Attempted to reset the LoggerFactory without possessing the guard."); - } - - if(selector == null) { - throw new IllegalArgumentException("RepositorySelector must be non-null."); - } - - LogManager.guard = guard; - LogManager.repositorySelector = selector; - } - - - /** - * This method tests if called from a method that - * is known to result in class members being abnormally - * set to null but is assumed to be harmless since the - * all classes are in the process of being unloaded. - * - * @param ex exception used to determine calling stack. - * @return true if calling stack is recognized as likely safe. - */ - private static boolean isLikelySafeScenario(final Exception ex) { - StringWriter stringWriter = new StringWriter(); - ex.printStackTrace(new PrintWriter(stringWriter)); - String msg = stringWriter.toString(); - return msg.indexOf("org.apache.catalina.loader.WebappClassLoader.stop") != -1; - } - - static - public - LoggerRepository getLoggerRepository() { - if (repositorySelector == null) { - repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository()); - guard = null; - Exception ex = new IllegalStateException("Class invariant violation"); - String msg = - "log4j called after unloading, see http://logging.apache.org/log4j/1.2/faq.html#unload."; - if (isLikelySafeScenario(ex)) { - LogLog.debug(msg, ex); - } else { - LogLog.error(msg, ex); - } - } - return repositorySelector.getLoggerRepository(); - } - - /** - Retrieve the appropriate root logger. - */ - public - static - Logger getRootLogger() { - // Delegate the actual manufacturing of the logger to the logger repository. - return getLoggerRepository().getRootLogger(); - } - - /** - Retrieve the appropriate {@link Logger} instance. - */ - public - static - Logger getLogger(final String name) { - // Delegate the actual manufacturing of the logger to the logger repository. - return getLoggerRepository().getLogger(name); - } - - /** - Retrieve the appropriate {@link Logger} instance. - */ - public - static - Logger getLogger(final Class clazz) { - // Delegate the actual manufacturing of the logger to the logger repository. - return getLoggerRepository().getLogger(clazz.getName()); - } - - - /** - Retrieve the appropriate {@link Logger} instance. - */ - public - static - Logger getLogger(final String name, final LoggerFactory factory) { - // Delegate the actual manufacturing of the logger to the logger repository. - return getLoggerRepository().getLogger(name, factory); - } - - public - static - Logger exists(final String name) { - return getLoggerRepository().exists(name); - } - - public - static - Enumeration getCurrentLoggers() { - return getLoggerRepository().getCurrentLoggers(); - } - - public - static - void shutdown() { - getLoggerRepository().shutdown(); - } - - public - static - void resetConfiguration() { - getLoggerRepository().resetConfiguration(); - } -} - diff --git a/java/src/org/apache/log4j/LogSF.java b/java/src/org/apache/log4j/LogSF.java deleted file mode 100644 index 8302e20..0000000 --- a/java/src/org/apache/log4j/LogSF.java +++ /dev/null @@ -1,1541 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j; - -import org.apache.log4j.spi.LoggingEvent; - -import java.util.ResourceBundle; - - -/** - * This class provides parameterized logging services - * using the SLF4J pattern syntax. - *

- * Message formatting is only performed when the - * request exceeds the threshold level of the logger. - * - * @since 1.2.16 - * - */ -public final class LogSF extends LogXF { - /** - * private constructor. - * - */ - private LogSF() { - } - - - - - /** - * Formats arguments using SLF4J-like formatter. - * @param pattern pattern, may be malformed. - * @param arguments arguments. - * @return Message string - */ - private static String format(final String pattern, - final Object[] arguments) { - if (pattern != null) { - String retval = ""; - int count = 0; - int prev = 0; - int pos = pattern.indexOf("{"); - while(pos >= 0) { - if (pos == 0 || pattern.charAt(pos-1) != '\\') { - retval += pattern.substring(prev, pos); - if (pos + 1 < pattern.length() && pattern.charAt(pos+1) == '}') { - if(arguments != null && count < arguments.length) { - retval += arguments[count++]; - } else { - retval += "{}"; - } - prev = pos + 2; - } else { - retval += "{"; - prev = pos + 1; - } - } else { - retval += pattern.substring(prev, pos - 1) + "{"; - prev = pos + 1; - } - pos = pattern.indexOf("{", prev); - } - return retval + pattern.substring(prev); - } - return null; - } - - /** - * Formats arguments using MessageFormat. - * @param pattern pattern, may be malformed. - * @param arg0 argument, may be null or mismatched. - * @return Message string - */ - private static String format(final String pattern, final Object arg0) { - if (pattern != null) { - // - // if there is an escaped brace, delegate to multi-param formatter - if (pattern.indexOf("\\{") >= 0) { - return format(pattern, new Object[] { arg0 }); - } - int pos = pattern.indexOf("{}"); - if (pos >= 0) { - return pattern.substring(0, pos) + arg0 + pattern.substring(pos+2); - } - } - return pattern; - } - - /** - * Formats arguments using MessageFormat using a pattern from - * a resource bundle. - * @param resourceBundleName name of resource bundle, may be null. - * @param key key for pattern in resource bundle, may be null. - * @param arguments arguments, may be null or mismatched. - * @return Message string or null - */ - private static String format( - final String resourceBundleName, - final String key, - final Object[] arguments) { - String pattern; - if (resourceBundleName != null) { - try { - ResourceBundle bundle = - ResourceBundle.getBundle(resourceBundleName); - pattern = bundle.getString(key); - } catch (Exception ex) { - pattern = key; - } - } else { - pattern = key; - } - return format(pattern, arguments); - } - - - /** - * Fully Qualified Class Name of this class. - */ - private static final String FQCN = LogSF.class.getName(); - - /** - * Equivalent of Logger.forcedLog. - * - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param msg message, may be null. - */ - private static void forcedLog(final Logger logger, - final Level level, - final String msg) { - logger.callAppenders(new LoggingEvent(FQCN, logger, level, msg, null)); - } - - /** - * Equivalent of Logger.forcedLog. - * - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param msg message, may be null. - * @param t throwable. - */ - private static void forcedLog(final Logger logger, - final Level level, - final String msg, - final Throwable t) { - logger.callAppenders(new LoggingEvent(FQCN, logger, level, msg, t)); - } - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be - * formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at error level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void error(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.ERROR)) { - forcedLog(logger, Level.ERROR, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at fatal level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void fatal(final Logger logger, final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.FATAL)) { - forcedLog(logger, Level.FATAL, format(pattern, arguments)); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be - * formatted and substituted. - */ - public static void trace(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, arguments), t); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void debug(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, arguments), t); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void info(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, arguments), t); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void warn(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, arguments), t); - } - } - - /** - * Log a parameterized message at error level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void error(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.ERROR)) { - forcedLog(logger, Level.ERROR, format(pattern, arguments), t); - } - } - - /** - * Log a parameterized message at fatal level. - * @param logger logger, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param arguments an array of arguments to be formatted and substituted. - */ - public static void fatal(final Logger logger, - final Throwable t, - final String pattern, - final Object[] arguments) { - if (logger.isEnabledFor(Level.FATAL)) { - forcedLog(logger, Level.FATAL, format(pattern, arguments), t); - } - } - - - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final boolean argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final char argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final byte argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final short argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final int argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final long argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final float argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final double argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final Object argument) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, format(pattern, argument)); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final Object arg0, final Object arg1) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, - format(pattern, toArray(arg0, arg1))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, - format(pattern, toArray(arg0, arg1, arg2))); - } - } - - /** - * Log a parameterized message at trace level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - * @param arg3 a value to be formatted and substituted. - */ - public static void trace(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2, - final Object arg3) { - if (logger.isEnabledFor(TRACE)) { - forcedLog(logger, TRACE, - format(pattern, toArray(arg0, arg1, arg2, arg3))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final boolean argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final char argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final byte argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final short argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final int argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final long argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final float argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final double argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final Object argument) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, format(pattern, argument)); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final Object arg0, final Object arg1) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, - format(pattern, toArray(arg0, arg1))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, - format(pattern, toArray(arg0, arg1, arg2))); - } - } - - /** - * Log a parameterized message at debug level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - * @param arg3 a value to be formatted and substituted. - */ - public static void debug(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2, - final Object arg3) { - if (logger.isDebugEnabled()) { - forcedLog(logger, Level.DEBUG, - format(pattern, toArray(arg0, arg1, arg2, arg3))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final boolean argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final char argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final byte argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final short argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final int argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final long argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final float argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final double argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final Object argument) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, argument)); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final Object arg0, final Object arg1) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, toArray(arg0, arg1))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, - toArray(arg0, arg1, arg2))); - } - } - - /** - * Log a parameterized message at info level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - * @param arg3 a value to be formatted and substituted. - */ - public static void info(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2, - final Object arg3) { - if (logger.isInfoEnabled()) { - forcedLog(logger, Level.INFO, format(pattern, - toArray(arg0, arg1, arg2, arg3))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final boolean argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final char argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final byte argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final short argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final int argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final long argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final float argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final double argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, valueOf(argument))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param argument a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final Object argument) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, argument)); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final Object arg0, final Object arg1) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, - format(pattern, toArray(arg0, arg1))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, - format(pattern, toArray(arg0, arg1, arg2))); - } - } - - /** - * Log a parameterized message at warn level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - * @param arg3 a value to be formatted and substituted. - */ - public static void warn(final Logger logger, final String pattern, - final Object arg0, final Object arg1, final Object arg2, - final Object arg3) { - if (logger.isEnabledFor(Level.WARN)) { - forcedLog(logger, Level.WARN, format(pattern, - toArray(arg0, arg1, arg2, arg3))); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param parameters parameters to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final Object[] parameters) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, parameters)); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param t throwable, may be null. - * @param pattern pattern, may be null. - * @param parameters parameters to the log message. - */ - public static void log(final Logger logger, - final Level level, - final Throwable t, - final String pattern, - final Object[] parameters) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, parameters), t); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final Object param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(param1))); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final boolean param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final byte param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final char param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final short param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final int param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final long param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final float param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param param1 parameter to the log message. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final double param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final Object arg0, final Object arg1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(arg0, arg1))); - } - } - - /** - * Log a parameterized message at specifed level. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param pattern pattern, may be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final Object arg0, final Object arg1, final Object arg2) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(pattern, toArray(arg0, arg1, arg2))); - } - } - - /** - * Log a parameterized message at specified level. - * @param logger logger, may not be null. - * @param pattern pattern, may be null. - * @param level level, may not be null. - * @param arg0 a value to be formatted and substituted. - * @param arg1 a value to be formatted and substituted. - * @param arg2 a value to be formatted and substituted. - * @param arg3 a value to be formatted and substituted. - */ - public static void log(final Logger logger, - final Level level, - final String pattern, - final Object arg0, final Object arg1, final Object arg2, - final Object arg3) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, format(pattern, - toArray(arg0, arg1, arg2, arg3))); - } - } - - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param parameters parameters to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final Object[] parameters) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, parameters)); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param t throwable, may be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param parameters parameters to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final Throwable t, - final String bundleName, - final String key, - final Object[] parameters) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, parameters), t); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final Object param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(param1))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final boolean param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final char param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final byte param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final short param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final int param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final long param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final float param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final double param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(valueOf(param1)))); - } - } - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param0 Parameter to the log message. - * @param param1 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final Object param0, - final Object param1) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(param0, param1))); - } - } - - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param0 Parameter to the log message. - * @param param1 Parameter to the log message. - * @param param2 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final Object param0, - final Object param1, - final Object param2) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, toArray(param0, param1, param2))); - } - } - - - /** - * Log a parameterized message using a pattern from a resource bundle. - * @param logger logger, may not be null. - * @param level level, may not be null. - * @param bundleName resource bundle name, may be null. - * @param key key, may be null. - * @param param0 Parameter to the log message. - * @param param1 Parameter to the log message. - * @param param2 Parameter to the log message. - * @param param3 Parameter to the log message. - */ - public static void logrb(final Logger logger, - final Level level, - final String bundleName, - final String key, - final Object param0, - final Object param1, - final Object param2, - final Object param3) { - if (logger.isEnabledFor(level)) { - forcedLog(logger, level, - format(bundleName, key, - toArray(param0, param1, param2, param3))); - } - } -} diff --git a/java/src/org/apache/log4j/LogXF.java b/java/src/org/apache/log4j/LogXF.java deleted file mode 100644 index de2b94b..0000000 --- a/java/src/org/apache/log4j/LogXF.java +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j; - -import org.apache.log4j.spi.LoggingEvent; - -/** - * This is a base class for LogMF and LogSF parameterized logging classes. - * - * - * @see org.apache.log4j.LogMF - * @see org.apache.log4j.LogSF - * @since 1.2.16 - */ -public abstract class LogXF { - /** - * Trace level. - */ - protected static final Level TRACE = new Level(5000, "TRACE", 7); - /** - * Fully Qualified Class Name of this class. - */ - private static final String FQCN = LogXF.class.getName(); - - protected LogXF() { - } - - /** - * Returns a Boolean instance representing the specified boolean. - * Boolean.valueOf was added in JDK 1.4. - * - * @param b a boolean value. - * @return a Boolean instance representing b. - */ - protected static Boolean valueOf(final boolean b) { - if (b) { - return Boolean.TRUE; - } - return Boolean.FALSE; - } - - /** - * Returns a Character instance representing the specified char. - * Character.valueOf was added in JDK 1.5. - * - * @param c a character value. - * @return a Character instance representing c. - */ - protected static Character valueOf(final char c) { - return new Character(c); - } - - /** - * Returns a Byte instance representing the specified byte. - * Byte.valueOf was added in JDK 1.5. - * - * @param b a byte value. - * @return a Byte instance representing b. - */ - protected static Byte valueOf(final byte b) { - return new Byte(b); - } - - /** - * Returns a Short instance representing the specified short. - * Short.valueOf was added in JDK 1.5. - * - * @param b a short value. - * @return a Byte instance representing b. - */ - protected static Short valueOf(final short b) { - return new Short(b); - } - - /** - * Returns an Integer instance representing the specified int. - * Integer.valueOf was added in JDK 1.5. - * - * @param b an int value. - * @return an Integer instance representing b. - */ - protected static Integer valueOf(final int b) { - return new Integer(b); - } - - /** - * Returns a Long instance representing the specified long. - * Long.valueOf was added in JDK 1.5. - * - * @param b a long value. - * @return a Long instance representing b. - */ - protected static Long valueOf(final long b) { - return new Long(b); - } - - /** - * Returns a Float instance representing the specified float. - * Float.valueOf was added in JDK 1.5. - * - * @param b a float value. - * @return a Float instance representing b. - */ - protected static Float valueOf(final float b) { - return new Float(b); - } - - /** - * Returns a Double instance representing the specified double. - * Double.valueOf was added in JDK 1.5. - * - * @param b a double value. - * @return a Byte instance representing b. - */ - protected static Double valueOf(final double b) { - return new Double(b); - } - - /** - * Create new array. - * - * @param param1 parameter 1. - * @return new array. - */ - protected static Object[] toArray(final Object param1) { - return new Object[]{ - param1 - }; - } - - /** - * Create new array. - * - * @param param1 parameter 1. - * @param param2 parameter 2. - * @return new array. - */ - protected static Object[] toArray(final Object param1, - final Object param2) { - return new Object[]{ - param1, param2 - }; - } - - /** - * Create new array. - * - * @param param1 parameter 1. - * @param param2 parameter 2. - * @param param3 parameter 3. - * @return new array. - */ - protected static Object[] toArray(final Object param1, - final Object param2, - final Object param3) { - return new Object[]{ - param1, param2, param3 - }; - } - - /** - * Create new array. - * - * @param param1 parameter 1. - * @param param2 parameter 2. - * @param param3 parameter 3. - * @param param4 parameter 4. - * @return new array. - */ - protected static Object[] toArray(final Object param1, - final Object param2, - final Object param3, - final Object param4) { - return new Object[]{ - param1, param2, param3, param4 - }; - } - - /** - * Log an entering message at DEBUG level. - * - * @param logger logger, may not be null. - * @param sourceClass source class, may be null. - * @param sourceMethod method, may be null. - */ - public static void entering(final Logger logger, - final String sourceClass, - final String sourceMethod) { - if (logger.isDebugEnabled()) { - logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG, - sourceClass + "." + sourceMethod + " ENTRY", null)); - } - } - - /** - * Log an entering message with a parameter at DEBUG level. - * - * @param logger logger, may not be null. - * @param sourceClass source class, may be null. - * @param sourceMethod method, may be null. - * @param param parameter, may be null. - */ - public static void entering(final Logger logger, - final String sourceClass, - final String sourceMethod, - final String param) { - if (logger.isDebugEnabled()) { - String msg = sourceClass + "." + sourceMethod + " ENTRY " + param; - logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG, - msg, null)); - } - } - - /** - * Log an entering message with a parameter at DEBUG level. - * - * @param logger logger, may not be null. - * @param sourceClass source class, may be null. - * @param sourceMethod method, may be null. - * @param param parameter, may be null. - */ - public static void entering(final Logger logger, - final String sourceClass, - final String sourceMethod, - final Object param) { - if (logger.isDebugEnabled()) { - String msg = sourceClass + "." + sourceMethod + " ENTRY "; - if (param == null) { - msg += "null"; - } else { - try { - msg += param; - } catch(Throwable ex) { - msg += "?"; - } - } - logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG, - msg, null)); - } - } - - /** - * Log an entering message with an array of parameters at DEBUG level. - * - * @param logger logger, may not be null. - * @param sourceClass source class, may be null. - * @param sourceMethod method, may be null. - * @param params parameters, may be null. - */ - public static void entering(final Logger logger, - final String sourceClass, - final String sourceMethod, - final Object[] params) { - if (logger.isDebugEnabled()) { - String msg = sourceClass + "." + sourceMethod + " ENTRY "; - if (params != null && params.length > 0) { - String delim = "{"; - for (int i = 0; i < params.length; i++) { - try { - msg += delim + params[i]; - } catch(Throwable ex) { - msg += delim + "?"; - } - delim = ","; - } - msg += "}"; - } else { - msg += "{}"; - } - logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG, - msg, null)); - } - } - - /** - * Log an exiting message at DEBUG level. - * - * @param logger logger, may not be null. - * @param sourceClass source class, may be null. - * @param sourceMethod method, may be null. - */ - public static void exiting(final Logger logger, - final String sourceClass, - final String sourceMethod) { - if (logger.isDebugEnabled()) { - logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG, - sourceClass + "." + sourceMethod + " RETURN", null)); - } - } - - /** - * Log an exiting message with result at DEBUG level. - * - * @param logger logger, may not be null. - * @param sourceClass source class, may be null. - * @param sourceMethod method, may be null. - * @param result result, may be null. - */ - public static void exiting( - final Logger logger, - final String sourceClass, - final String sourceMethod, - final String result) { - if (logger.isDebugEnabled()) { - logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG, - sourceClass + "." + sourceMethod + " RETURN " + result, null)); - } - } - - /** - * Log an exiting message with result at DEBUG level. - * - * @param logger logger, may not be null. - * @param sourceClass source class, may be null. - * @param sourceMethod method, may be null. - * @param result result, may be null. - */ - public static void exiting( - final Logger logger, - final String sourceClass, - final String sourceMethod, - final Object result) { - if (logger.isDebugEnabled()) { - String msg = sourceClass + "." + sourceMethod + " RETURN "; - if (result == null) { - msg += "null"; - } else { - try { - msg += result; - } catch(Throwable ex) { - msg += "?"; - } - } - logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG, - msg, null)); - } - } - - /** - * Logs a throwing message at DEBUG level. - * - * @param logger logger, may not be null. - * @param sourceClass source class, may be null. - * @param sourceMethod method, may be null. - * @param thrown throwable, may be null. - */ - public static void throwing( - final Logger logger, - final String sourceClass, - final String sourceMethod, - final Throwable thrown) { - if (logger.isDebugEnabled()) { - logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG, - sourceClass + "." + sourceMethod + " THROW", thrown)); - } - } -} diff --git a/java/src/org/apache/log4j/Logger.java b/java/src/org/apache/log4j/Logger.java deleted file mode 100644 index 957145f..0000000 --- a/java/src/org/apache/log4j/Logger.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.spi.LoggerFactory; - - -/** - This is the central class in the log4j package. Most logging - operations, except configuration, are done through this class. - - @since log4j 1.2 - - @author Ceki Gülcü */ -public class Logger extends Category { - - /** - The fully qualified name of the Logger class. See also the - getFQCN method. */ - private static final String FQCN = Logger.class.getName(); - - - protected - Logger(String name) { - super(name); - } - - /** - Log a message object with the {@link Level#FINE FINE} level which - is just an alias for the {@link Level#DEBUG DEBUG} level. - -

This method first checks if this category is DEBUG - enabled by comparing the level of this category with the {@link - Level#DEBUG DEBUG} level. If this category is - DEBUG enabled, then it converts the message object - (passed as parameter) to a string by invoking the appropriate - {@link org.apache.log4j.or.ObjectRenderer}. It then proceeds to call all the - registered appenders in this category and also higher in the - hierarchy depending on the value of the additivity flag. - -

WARNING Note that passing a {@link Throwable} to this - method will print the name of the Throwable but no - stack trace. To print a stack trace use the {@link #debug(Object, - Throwable)} form instead. - - @param message the message object to log. */ - //public - //void fine(Object message) { - // if(repository.isDisabled(Level.DEBUG_INT)) - // return; - // if(Level.DEBUG.isGreaterOrEqual(this.getChainedLevel())) { - // forcedLog(FQCN, Level.DEBUG, message, null); - // } - //} - - - /** - Log a message object with the FINE level including - the stack trace of the {@link Throwable} t passed as - parameter. - -

See {@link #fine(Object)} form for more detailed information. - - @param message the message object to log. - @param t the exception to log, including its stack trace. */ - //public - //void fine(Object message, Throwable t) { - // if(repository.isDisabled(Level.DEBUG_INT)) - // return; - // if(Level.DEBUG.isGreaterOrEqual(this.getChainedLevel())) - // forcedLog(FQCN, Level.FINE, message, t); - //} - - /** - * Retrieve a logger named according to the value of the - * name parameter. If the named logger already exists, - * then the existing instance will be returned. Otherwise, a new - * instance is created. - * - *

By default, loggers do not have a set level but inherit it - * from their neareast ancestor with a set level. This is one of the - * central features of log4j. - * - * @param name The name of the logger to retrieve. - */ - static - public - Logger getLogger(String name) { - return LogManager.getLogger(name); - } - - /** - * Shorthand for getLogger(clazz.getName()). - * - * @param clazz The name of clazz will be used as the - * name of the logger to retrieve. See {@link #getLogger(String)} - * for more detailed information. - */ - static - public - Logger getLogger(Class clazz) { - return LogManager.getLogger(clazz.getName()); - } - - - /** - * Return the root logger for the current logger repository. - *

- * The {@link #getName Logger.getName()} method for the root logger always returns - * stirng value: "root". However, calling - * Logger.getLogger("root") does not retrieve the root - * logger but a logger just under root named "root". - *

- * In other words, calling this method is the only way to retrieve the - * root logger. - */ - public - static - Logger getRootLogger() { - return LogManager.getRootLogger(); - } - - /** - Like {@link #getLogger(String)} except that the type of logger - instantiated depends on the type returned by the {@link - LoggerFactory#makeNewLoggerInstance} method of the - factory parameter. - -

This method is intended to be used by sub-classes. - - @param name The name of the logger to retrieve. - - @param factory A {@link LoggerFactory} implementation that will - actually create a new Instance. - - @since 0.8.5 */ - public - static - Logger getLogger(String name, LoggerFactory factory) { - return LogManager.getLogger(name, factory); - } - - /** - * Log a message object with the {@link org.apache.log4j.Level#TRACE TRACE} level. - * - * @param message the message object to log. - * @see #debug(Object) for an explanation of the logic applied. - * @since 1.2.12 - */ - public void trace(Object message) { - if (repository.isDisabled(Level.TRACE_INT)) { - return; - } - - if (Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel())) { - forcedLog(FQCN, Level.TRACE, message, null); - } - } - - /** - * Log a message object with the TRACE level including the - * stack trace of the {@link Throwable}t passed as parameter. - * - *

- * See {@link #debug(Object)} form for more detailed information. - *

- * - * @param message the message object to log. - * @param t the exception to log, including its stack trace. - * @since 1.2.12 - */ - public void trace(Object message, Throwable t) { - if (repository.isDisabled(Level.TRACE_INT)) { - return; - } - - if (Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel())) { - forcedLog(FQCN, Level.TRACE, message, t); - } - } - - /** - * Check whether this category is enabled for the TRACE Level. - * @since 1.2.12 - * - * @return boolean - true if this category is enabled for level - * TRACE, false otherwise. - */ - public boolean isTraceEnabled() { - if (repository.isDisabled(Level.TRACE_INT)) { - return false; - } - - return Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel()); - } - -} diff --git a/java/src/org/apache/log4j/MDC.java b/java/src/org/apache/log4j/MDC.java deleted file mode 100644 index 5d861af..0000000 --- a/java/src/org/apache/log4j/MDC.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import java.util.Hashtable; -import org.apache.log4j.helpers.Loader; -import org.apache.log4j.helpers.ThreadLocalMap; - -/** - The MDC class is similar to the {@link NDC} class except that it is - based on a map instead of a stack. It provides mapped - diagnostic contexts. A Mapped Diagnostic Context, or - MDC in short, is an instrument for distinguishing interleaved log - output from different sources. Log output is typically interleaved - when a server handles multiple clients near-simultaneously. - -

The MDC is managed on a per thread basis. A - child thread automatically inherits a copy of the mapped - diagnostic context of its parent. - -

The MDC class requires JDK 1.2 or above. Under JDK 1.1 the MDC - will always return empty values but otherwise will not affect or - harm your application. - - @since 1.2 - - @author Ceki Gülcü */ -public class MDC { - - final static MDC mdc = new MDC(); - - static final int HT_SIZE = 7; - - boolean java1; - - Object tlm; - - private - MDC() { - java1 = Loader.isJava1(); - if(!java1) { - tlm = new ThreadLocalMap(); - } - } - - /** - Put a context value (the o parameter) as identified - with the key parameter into the current thread's - context map. - -

If the current thread does not have a context map it is - created as a side effect. - - */ - static - public - void put(String key, Object o) { - if (mdc != null) { - mdc.put0(key, o); - } - } - - /** - Get the context identified by the key parameter. - -

This method has no side effects. - */ - static - public - Object get(String key) { - if (mdc != null) { - return mdc.get0(key); - } - return null; - } - - /** - Remove the the context identified by the key - parameter. - - */ - static - public - void remove(String key) { - if (mdc != null) { - mdc.remove0(key); - } - } - - - /** - * Get the current thread's MDC as a hashtable. This method is - * intended to be used internally. - * */ - public static Hashtable getContext() { - if (mdc != null) { - return mdc.getContext0(); - } else { - return null; - } - } - - /** - * Remove all values from the MDC. - * @since 1.2.16 - */ - public static void clear() { - if (mdc != null) { - mdc.clear0(); - } - } - - - private - void put0(String key, Object o) { - if(java1 || tlm == null) { - return; - } else { - Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get(); - if(ht == null) { - ht = new Hashtable(HT_SIZE); - ((ThreadLocalMap)tlm).set(ht); - } - ht.put(key, o); - } - } - - private - Object get0(String key) { - if(java1 || tlm == null) { - return null; - } else { - Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get(); - if(ht != null && key != null) { - return ht.get(key); - } else { - return null; - } - } - } - - private - void remove0(String key) { - if(!java1 && tlm != null) { - Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get(); - if(ht != null) { - ht.remove(key); - } - } - } - - - private - Hashtable getContext0() { - if(java1 || tlm == null) { - return null; - } else { - return (Hashtable) ((ThreadLocalMap)tlm).get(); - } - } - - private - void clear0() { - if(!java1 && tlm != null) { - Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get(); - if(ht != null) { - ht.clear(); - } - } - } - -} diff --git a/java/src/org/apache/log4j/NDC.java b/java/src/org/apache/log4j/NDC.java deleted file mode 100644 index 2b8b8be..0000000 --- a/java/src/org/apache/log4j/NDC.java +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contributors: Dan Milstein -// Ray Millard - -package org.apache.log4j; - -import java.util.Hashtable; -import java.util.Stack; -import java.util.Enumeration; -import java.util.Vector; - -import org.apache.log4j.helpers.LogLog; - -/** - The NDC class implements nested diagnostic contexts as - defined by Neil Harrison in the article "Patterns for Logging - Diagnostic Messages" part of the book "Pattern Languages of - Program Design 3" edited by Martin et al. - -

A Nested Diagnostic Context, or NDC in short, is an instrument - to distinguish interleaved log output from different sources. Log - output is typically interleaved when a server handles multiple - clients near-simultaneously. - -

Interleaved log output can still be meaningful if each log entry - from different contexts had a distinctive stamp. This is where NDCs - come into play. - -

Note that NDCs are managed on a per thread - basis. NDC operations such as {@link #push push}, {@link - #pop}, {@link #clear}, {@link #getDepth} and {@link #setMaxDepth} - affect the NDC of the current thread only. NDCs of other - threads remain unaffected. - -

For example, a servlet can build a per client request NDC - consisting the clients host name and other information contained in - the the request. Cookies are another source of distinctive - information. To build an NDC one uses the {@link #push push} - operation. Simply put, - -

    -
  • Contexts can be nested. - -

  • When entering a context, call NDC.push. As a - side effect, if there is no nested diagnostic context for the - current thread, this method will create it. - -

  • When leaving a context, call NDC.pop. - -

  • When exiting a thread make sure to call {@link #remove - NDC.remove()}. -
- -

There is no penalty for forgetting to match each - push operation with a corresponding pop, - except the obvious mismatch between the real application context - and the context set in the NDC. - -

If configured to do so, {@link PatternLayout} and {@link - TTCCLayout} instances automatically retrieve the nested diagnostic - context for the current thread without any user intervention. - Hence, even if a servlet is serving multiple clients - simultaneously, the logs emanating from the same code (belonging to - the same category) can still be distinguished because each client - request will have a different NDC tag. - -

Heavy duty systems should call the {@link #remove} method when - leaving the running method of a thread. This ensures that the memory - used by the thread can be freed by the Java garbage - collector. There is a mechanism to lazily remove references to dead - threads. In practice, this means that you can be a little sloppy - and sometimes forget to call {@link #remove} before exiting a - thread. - -

A thread may inherit the nested diagnostic context of another - (possibly parent) thread using the {@link #inherit inherit} - method. A thread may obtain a copy of its NDC with the {@link - #cloneStack cloneStack} method and pass the reference to any other - thread, in particular to a child. - - @author Ceki Gülcü - @since 0.7.0 - -*/ - -public class NDC { - - // The synchronized keyword is not used in this class. This may seem - // dangerous, especially since the class will be used by - // multiple-threads. In particular, all threads share the same - // hashtable (the "ht" variable). This is OK since java hashtables - // are thread safe. Same goes for Stacks. - - // More importantly, when inheriting diagnostic contexts the child - // thread is handed a clone of the parent's NDC. It follows that - // each thread has its own NDC (i.e. stack). - - static Hashtable ht = new Hashtable(); - - static int pushCounter = 0; // the number of times push has been called - // after the latest call to lazyRemove - - // The number of times we allow push to be called before we call lazyRemove - // 5 is a relatively small number. As such, lazyRemove is not called too - // frequently. We thus avoid the cost of creating an Enumeration too often. - // The higher this number, the longer is the avarage period for which all - // logging calls in all threads are blocked. - static final int REAP_THRESHOLD = 5; - - // No instances allowed. - private NDC() {} - - /** - * Get NDC stack for current thread. - * @return NDC stack for current thread. - */ - private static Stack getCurrentStack() { - if (ht != null) { - return (Stack) ht.get(Thread.currentThread()); - } - return null; - } - - - /** - Clear any nested diagnostic information if any. This method is - useful in cases where the same thread can be potentially used - over and over in different unrelated contexts. - -

This method is equivalent to calling the {@link #setMaxDepth} - method with a zero maxDepth argument. - - @since 0.8.4c */ - public - static - void clear() { - Stack stack = getCurrentStack(); - if(stack != null) - stack.setSize(0); - } - - - /** - Clone the diagnostic context for the current thread. - -

Internally a diagnostic context is represented as a stack. A - given thread can supply the stack (i.e. diagnostic context) to a - child thread so that the child can inherit the parent thread's - diagnostic context. - -

The child thread uses the {@link #inherit inherit} method to - inherit the parent's diagnostic context. - - @return Stack A clone of the current thread's diagnostic context. - - */ - public - static - Stack cloneStack() { - Stack stack = getCurrentStack(); - if(stack == null) - return null; - else { - return (Stack) stack.clone(); - } - } - - - /** - Inherit the diagnostic context of another thread. - -

The parent thread can obtain a reference to its diagnostic - context using the {@link #cloneStack} method. It should - communicate this information to its child so that it may inherit - the parent's diagnostic context. - -

The parent's diagnostic context is cloned before being - inherited. In other words, once inherited, the two diagnostic - contexts can be managed independently. - -

In java, a child thread cannot obtain a reference to its - parent, unless it is directly handed the reference. Consequently, - there is no client-transparent way of inheriting diagnostic - contexts. Do you know any solution to this problem? - - @param stack The diagnostic context of the parent thread. - - */ - public - static - void inherit(Stack stack) { - if(stack != null) - ht.put(Thread.currentThread(), stack); - } - - - /** - Never use this method directly, use the {@link - org.apache.log4j.spi.LoggingEvent#getNDC} method instead. - */ - static - public - String get() { - Stack s = getCurrentStack(); - if(s != null && !s.isEmpty()) - return ((DiagnosticContext) s.peek()).fullMessage; - else - return null; - } - - /** - * Get the current nesting depth of this diagnostic context. - * - * @see #setMaxDepth - * @since 0.7.5 - */ - public - static - int getDepth() { - Stack stack = getCurrentStack(); - if(stack == null) - return 0; - else - return stack.size(); - } - - private - static - void lazyRemove() { - if (ht == null) return; - - // The synchronization on ht is necessary to prevent JDK 1.2.x from - // throwing ConcurrentModificationExceptions at us. This sucks BIG-TIME. - // One solution is to write our own hashtable implementation. - Vector v; - - synchronized(ht) { - // Avoid calling clean-up too often. - if(++pushCounter <= REAP_THRESHOLD) { - return; // We release the lock ASAP. - } else { - pushCounter = 0; // OK let's do some work. - } - - int misses = 0; - v = new Vector(); - Enumeration enumeration = ht.keys(); - // We give up after 4 straigt missses. That is 4 consecutive - // inspected threads in 'ht' that turn out to be alive. - // The higher the proportion on dead threads in ht, the higher the - // chances of removal. - while(enumeration.hasMoreElements() && (misses <= 4)) { - Thread t = (Thread) enumeration.nextElement(); - if(t.isAlive()) { - misses++; - } else { - misses = 0; - v.addElement(t); - } - } - } // synchronized - - int size = v.size(); - for(int i = 0; i < size; i++) { - Thread t = (Thread) v.elementAt(i); - LogLog.debug("Lazy NDC removal for thread [" + t.getName() + "] ("+ - ht.size() + ")."); - ht.remove(t); - } - } - - /** - Clients should call this method before leaving a diagnostic - context. - -

The returned value is the value that was pushed last. If no - context is available, then the empty string "" is returned. - - @return String The innermost diagnostic context. - - */ - public - static - String pop() { - Stack stack = getCurrentStack(); - if(stack != null && !stack.isEmpty()) - return ((DiagnosticContext) stack.pop()).message; - else - return ""; - } - - /** - Looks at the last diagnostic context at the top of this NDC - without removing it. - -

The returned value is the value that was pushed last. If no - context is available, then the empty string "" is returned. - - @return String The innermost diagnostic context. - - */ - public - static - String peek() { - Stack stack = getCurrentStack(); - if(stack != null && !stack.isEmpty()) - return ((DiagnosticContext) stack.peek()).message; - else - return ""; - } - - /** - Push new diagnostic context information for the current thread. - -

The contents of the message parameter is - determined solely by the client. - - @param message The new diagnostic context information. */ - public - static - void push(String message) { - Stack stack = getCurrentStack(); - - if(stack == null) { - DiagnosticContext dc = new DiagnosticContext(message, null); - stack = new Stack(); - Thread key = Thread.currentThread(); - ht.put(key, stack); - stack.push(dc); - } else if (stack.isEmpty()) { - DiagnosticContext dc = new DiagnosticContext(message, null); - stack.push(dc); - } else { - DiagnosticContext parent = (DiagnosticContext) stack.peek(); - stack.push(new DiagnosticContext(message, parent)); - } - } - - /** - Remove the diagnostic context for this thread. - -

Each thread that created a diagnostic context by calling - {@link #push} should call this method before exiting. Otherwise, - the memory used by the thread cannot be reclaimed by the - VM. - -

As this is such an important problem in heavy duty systems and - because it is difficult to always guarantee that the remove - method is called before exiting a thread, this method has been - augmented to lazily remove references to dead threads. In - practice, this means that you can be a little sloppy and - occasionally forget to call {@link #remove} before exiting a - thread. However, you must call remove sometime. If - you never call it, then your application is sure to running out of - memory. - - */ - static - public - void remove() { - if (ht != null) { - ht.remove(Thread.currentThread()); - - // Lazily remove dead-thread references in ht. - lazyRemove(); - } - } - - /** - Set maximum depth of this diagnostic context. If the current - depth is smaller or equal to maxDepth, then no - action is taken. - -

This method is a convenient alternative to multiple {@link - #pop} calls. Moreover, it is often the case that at the end of - complex call sequences, the depth of the NDC is - unpredictable. The setMaxDepth method circumvents - this problem. - -

For example, the combination -

-       void foo() {
-          int depth = NDC.getDepth();
-
-          ... complex sequence of calls
-
-          NDC.setMaxDepth(depth);
-       }
-     
- - ensures that between the entry and exit of foo the depth of the - diagnostic stack is conserved. - - @see #getDepth - @since 0.7.5 */ - static - public - void setMaxDepth(int maxDepth) { - Stack stack = getCurrentStack(); - if(stack != null && maxDepth < stack.size()) - stack.setSize(maxDepth); - } - - // ===================================================================== - private static class DiagnosticContext { - - String fullMessage; - String message; - - DiagnosticContext(String message, DiagnosticContext parent) { - this.message = message; - if(parent != null) { - fullMessage = parent.fullMessage + ' ' + message; - } else { - fullMessage = message; - } - } - } -} - diff --git a/java/src/org/apache/log4j/PatternLayout.java b/java/src/org/apache/log4j/PatternLayout.java deleted file mode 100644 index d668a75..0000000 --- a/java/src/org/apache/log4j/PatternLayout.java +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.helpers.PatternParser; -import org.apache.log4j.helpers.PatternConverter; - - -// Contributors: Nelson Minar -// Anders Kristensen - -/** - - A flexible layout configurable with pattern string. - - This code is known to have synchronization and other issues - which are not present in org.apache.log4j.EnhancedPatternLayout. - EnhancedPatternLayout should be used in preference to PatternLayout. - EnhancedPatternLayout is distributed in the log4j extras companion. - -

The goal of this class is to {@link #format format} a {@link - LoggingEvent} and return the results as a String. The results - depend on the conversion pattern. - -

The conversion pattern is closely related to the conversion - pattern of the printf function in C. A conversion pattern is - composed of literal text and format control expressions called - conversion specifiers. - -

You are free to insert any literal text within the conversion - pattern. - -

Each conversion specifier starts with a percent sign (%) and is - followed by optional format modifiers and a conversion - character. The conversion character specifies the type of - data, e.g. category, priority, date, thread name. The format - modifiers control such things as field width, padding, left and - right justification. The following is a simple example. - -

Let the conversion pattern be "%-5p [%t]: %m%n" and assume - that the log4j environment was set to use a PatternLayout. Then the - statements -

-   Category root = Category.getRoot();
-   root.debug("Message 1");
-   root.warn("Message 2");
-   
- would yield the output -
-   DEBUG [main]: Message 1
-   WARN  [main]: Message 2
-   
- -

Note that there is no explicit separator between text and - conversion specifiers. The pattern parser knows when it has reached - the end of a conversion specifier when it reads a conversion - character. In the example above the conversion specifier - %-5p means the priority of the logging event should be left - justified to a width of five characters. - - The recognized conversion characters are - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Conversion CharacterEffect
cUsed to output the category of the logging event. The - category conversion specifier can be optionally followed by - precision specifier, that is a decimal constant in - brackets. - -

If a precision specifier is given, then only the corresponding - number of right most components of the category name will be - printed. By default the category name is printed in full. - -

For example, for the category name "a.b.c" the pattern - %c{2} will output "b.c". - -

CUsed to output the fully qualified class name of the caller - issuing the logging request. This conversion specifier - can be optionally followed by precision specifier, that - is a decimal constant in brackets. - -

If a precision specifier is given, then only the corresponding - number of right most components of the class name will be - printed. By default the class name is output in fully qualified form. - -

For example, for the class name "org.apache.xyz.SomeClass", the - pattern %C{1} will output "SomeClass". - -

WARNING Generating the caller class information is - slow. Thus, use should be avoided unless execution speed is - not an issue. - -

d Used to output the date of - the logging event. The date conversion specifier may be - followed by a date format specifier enclosed between - braces. For example, %d{HH:mm:ss,SSS} or - %d{dd MMM yyyy HH:mm:ss,SSS}. If no - date format specifier is given then ISO8601 format is - assumed. - -

The date format specifier admits the same syntax as the - time pattern string of the {@link - java.text.SimpleDateFormat}. Although part of the standard - JDK, the performance of SimpleDateFormat is - quite poor. - -

For better results it is recommended to use the log4j date - formatters. These can be specified using one of the strings - "ABSOLUTE", "DATE" and "ISO8601" for specifying {@link - org.apache.log4j.helpers.AbsoluteTimeDateFormat - AbsoluteTimeDateFormat}, {@link - org.apache.log4j.helpers.DateTimeDateFormat DateTimeDateFormat} - and respectively {@link - org.apache.log4j.helpers.ISO8601DateFormat - ISO8601DateFormat}. For example, %d{ISO8601} or - %d{ABSOLUTE}. - -

These dedicated date formatters perform significantly - better than {@link java.text.SimpleDateFormat}. -

FUsed to output the file name where the logging request was - issued. - -

WARNING Generating caller location information is - extremely slow and should be avoided unless execution speed - is not an issue. - -

lUsed to output location information of the caller which generated - the logging event. - -

The location information depends on the JVM implementation but - usually consists of the fully qualified name of the calling - method followed by the callers source the file name and line - number between parentheses. - -

The location information can be very useful. However, its - generation is extremely slow and should be avoided - unless execution speed is not an issue. - -

LUsed to output the line number from where the logging request - was issued. - -

WARNING Generating caller location information is - extremely slow and should be avoided unless execution speed - is not an issue. - -

mUsed to output the application supplied message associated with - the logging event.
MUsed to output the method name where the logging request was - issued. - -

WARNING Generating caller location information is - extremely slow and should be avoided unless execution speed - is not an issue. - -

nOutputs the platform dependent line separator character or - characters. - -

This conversion character offers practically the same - performance as using non-portable line separator strings such as - "\n", or "\r\n". Thus, it is the preferred way of specifying a - line separator. - - -

pUsed to output the priority of the logging event.
rUsed to output the number of milliseconds elapsed from the construction - of the layout until the creation of the logging event.
tUsed to output the name of the thread that generated the - logging event.
xUsed to output the NDC (nested diagnostic context) associated - with the thread that generated the logging event. -
X - -

Used to output the MDC (mapped diagnostic context) associated - with the thread that generated the logging event. The X - conversion character must be followed by the key for the - map placed between braces, as in %X{clientNumber} where - clientNumber is the key. The value in the MDC - corresponding to the key will be output.

- -

See {@link MDC} class for more details. -

- -
%The sequence %% outputs a single percent sign. -
- -

By default the relevant information is output as is. However, - with the aid of format modifiers it is possible to change the - minimum field width, the maximum field width and justification. - -

The optional format modifier is placed between the percent sign - and the conversion character. - -

The first optional format modifier is the left justification - flag which is just the minus (-) character. Then comes the - optional minimum field width modifier. This is a decimal - constant that represents the minimum number of characters to - output. If the data item requires fewer characters, it is padded on - either the left or the right until the minimum width is - reached. The default is to pad on the left (right justify) but you - can specify right padding with the left justification flag. The - padding character is space. If the data item is larger than the - minimum field width, the field is expanded to accommodate the - data. The value is never truncated. - -

This behavior can be changed using the maximum field - width modifier which is designated by a period followed by a - decimal constant. If the data item is longer than the maximum - field, then the extra characters are removed from the - beginning of the data item and not from the end. For - example, it the maximum field width is eight and the data item is - ten characters long, then the first two characters of the data item - are dropped. This behavior deviates from the printf function in C - where truncation is done from the end. - -

Below are various format modifier examples for the category - conversion specifier. - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
Format modifier - left justify - minimum width - maximum width - comment - -
%20cfalse20noneLeft pad with spaces if the category name is less than 20 - characters long. - -
%-20c true 20 none Right pad with - spaces if the category name is less than 20 characters long. - -
%.30cNAnone30Truncate from the beginning if the category name is longer than 30 - characters. - -
%20.30cfalse2030Left pad with spaces if the category name is shorter than 20 - characters. However, if category name is longer than 30 characters, - then truncate from the beginning. - -
%-20.30ctrue2030Right pad with spaces if the category name is shorter than 20 - characters. However, if category name is longer than 30 characters, - then truncate from the beginning. - -
- -

Below are some examples of conversion patterns. - -

- -

%r [%t] %-5p %c %x - %m%n -

This is essentially the TTCC layout. - -

%-6r [%15.15t] %-5p %30.30c %x - %m%n - -

Similar to the TTCC layout except that the relative time is - right padded if less than 6 digits, thread name is right padded if - less than 15 characters and truncated if longer and the category - name is left padded if shorter than 30 characters and truncated if - longer. - -
- -

The above text is largely inspired from Peter A. Darnell and - Philip E. Margolis' highly recommended book "C -- a Software - Engineering Approach", ISBN 0-387-97389-3. - - @author James P. Cakalic - @author Ceki Gülcü - - - @since 0.8.2 */ -public class PatternLayout extends Layout { - - - /** Default pattern string for log output. Currently set to the - string "%m%n" which just prints the application supplied - message. */ - public final static String DEFAULT_CONVERSION_PATTERN ="%m%n"; - - /** A conversion pattern equivalent to the TTCCCLayout. - Current value is %r [%t] %p %c %x - %m%n. */ - public final static String TTCC_CONVERSION_PATTERN - = "%r [%t] %p %c %x - %m%n"; - - - protected final int BUF_SIZE = 256; - protected final int MAX_CAPACITY = 1024; - - - // output buffer appended to when format() is invoked - private StringBuffer sbuf = new StringBuffer(BUF_SIZE); - - private String pattern; - - private PatternConverter head; - - /** - Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN. - - The default pattern just produces the application supplied message. - */ - public PatternLayout() { - this(DEFAULT_CONVERSION_PATTERN); - } - - /** - Constructs a PatternLayout using the supplied conversion pattern. - */ - public PatternLayout(String pattern) { - this.pattern = pattern; - head = createPatternParser((pattern == null) ? DEFAULT_CONVERSION_PATTERN : - pattern).parse(); - } - - /** - Set the ConversionPattern option. This is the string which - controls formatting and consists of a mix of literal content and - conversion specifiers. - */ - public - void setConversionPattern(String conversionPattern) { - pattern = conversionPattern; - head = createPatternParser(conversionPattern).parse(); - } - - /** - Returns the value of the ConversionPattern option. - */ - public - String getConversionPattern() { - return pattern; - } - - /** - Does not do anything as options become effective - */ - public - void activateOptions() { - // nothing to do. - } - - /** - The PatternLayout does not handle the throwable contained within - {@link LoggingEvent LoggingEvents}. Thus, it returns - true. - - @since 0.8.4 */ - public - boolean ignoresThrowable() { - return true; - } - - /** - Returns PatternParser used to parse the conversion string. Subclasses - may override this to return a subclass of PatternParser which recognize - custom conversion characters. - - @since 0.9.0 - */ - protected PatternParser createPatternParser(String pattern) { - return new PatternParser(pattern); - } - - - /** - Produces a formatted string as specified by the conversion pattern. - */ - public String format(LoggingEvent event) { - // Reset working stringbuffer - if(sbuf.capacity() > MAX_CAPACITY) { - sbuf = new StringBuffer(BUF_SIZE); - } else { - sbuf.setLength(0); - } - - PatternConverter c = head; - - while(c != null) { - c.format(sbuf, event); - c = c.next; - } - return sbuf.toString(); - } -} diff --git a/java/src/org/apache/log4j/Priority.java b/java/src/org/apache/log4j/Priority.java deleted file mode 100644 index 53e94b4..0000000 --- a/java/src/org/apache/log4j/Priority.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contributors: Kitching Simon - -package org.apache.log4j; - -/** - Refrain from using this class directly, use - the {@link Level} class instead. - - @author Ceki Gülcü */ -public class Priority { - - transient int level; - transient String levelStr; - transient int syslogEquivalent; - - public final static int OFF_INT = Integer.MAX_VALUE; - public final static int FATAL_INT = 50000; - public final static int ERROR_INT = 40000; - public final static int WARN_INT = 30000; - public final static int INFO_INT = 20000; - public final static int DEBUG_INT = 10000; - //public final static int FINE_INT = DEBUG_INT; - public final static int ALL_INT = Integer.MIN_VALUE; - - /** - * @deprecated Use {@link Level#FATAL} instead. - */ - final static public Priority FATAL = new Level(FATAL_INT, "FATAL", 0); - - /** - * @deprecated Use {@link Level#ERROR} instead. - */ - final static public Priority ERROR = new Level(ERROR_INT, "ERROR", 3); - - /** - * @deprecated Use {@link Level#WARN} instead. - */ - final static public Priority WARN = new Level(WARN_INT, "WARN", 4); - - /** - * @deprecated Use {@link Level#INFO} instead. - */ - final static public Priority INFO = new Level(INFO_INT, "INFO", 6); - - /** - * @deprecated Use {@link Level#DEBUG} instead. - */ - final static public Priority DEBUG = new Level(DEBUG_INT, "DEBUG", 7); - - - /** - * Default constructor for deserialization. - */ - protected Priority() { - level = DEBUG_INT; - levelStr = "DEBUG"; - syslogEquivalent = 7; - } - - /** - Instantiate a level object. - */ - protected - Priority(int level, String levelStr, int syslogEquivalent) { - this.level = level; - this.levelStr = levelStr; - this.syslogEquivalent = syslogEquivalent; - } - - /** - Two priorities are equal if their level fields are equal. - @since 1.2 - */ - public - boolean equals(Object o) { - if(o instanceof Priority) { - Priority r = (Priority) o; - return (this.level == r.level); - } else { - return false; - } - } - - /** - Return the syslog equivalent of this priority as an integer. - */ - public - final - int getSyslogEquivalent() { - return syslogEquivalent; - } - - - - /** - Returns true if this level has a higher or equal - level than the level passed as argument, false - otherwise. - -

You should think twice before overriding the default - implementation of isGreaterOrEqual method. - - */ - public - boolean isGreaterOrEqual(Priority r) { - return level >= r.level; - } - - /** - Return all possible priorities as an array of Level objects in - descending order. - - @deprecated This method will be removed with no replacement. - */ - public - static - Priority[] getAllPossiblePriorities() { - return new Priority[] {Priority.FATAL, Priority.ERROR, Level.WARN, - Priority.INFO, Priority.DEBUG}; - } - - - /** - Returns the string representation of this priority. - */ - final - public - String toString() { - return levelStr; - } - - /** - Returns the integer representation of this level. - */ - public - final - int toInt() { - return level; - } - - /** - * @deprecated Please use the {@link Level#toLevel(String)} method instead. - */ - public - static - Priority toPriority(String sArg) { - return Level.toLevel(sArg); - } - - /** - * @deprecated Please use the {@link Level#toLevel(int)} method instead. - */ - public - static - Priority toPriority(int val) { - return toPriority(val, Priority.DEBUG); - } - - /** - * @deprecated Please use the {@link Level#toLevel(int, Level)} method instead. - */ - public - static - Priority toPriority(int val, Priority defaultPriority) { - return Level.toLevel(val, (Level) defaultPriority); - } - - /** - * @deprecated Please use the {@link Level#toLevel(String, Level)} method instead. - */ - public - static - Priority toPriority(String sArg, Priority defaultPriority) { - return Level.toLevel(sArg, (Level) defaultPriority); - } -} diff --git a/java/src/org/apache/log4j/PropertyConfigurator.java b/java/src/org/apache/log4j/PropertyConfigurator.java deleted file mode 100644 index 5672344..0000000 --- a/java/src/org/apache/log4j/PropertyConfigurator.java +++ /dev/null @@ -1,963 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -// Contibutors: "Luke Blanshard" -// "Mark DONSZELMANN" -// Anders Kristensen - -package org.apache.log4j; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InterruptedIOException; -import java.net.URLConnection; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Properties; -import java.util.StringTokenizer; -import java.util.Vector; -import java.util.Iterator; -import java.util.Map; - -import org.apache.log4j.config.PropertySetter; -import org.apache.log4j.helpers.FileWatchdog; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.helpers.OptionConverter; -import org.apache.log4j.or.RendererMap; -import org.apache.log4j.spi.Configurator; -import org.apache.log4j.spi.Filter; -import org.apache.log4j.spi.LoggerFactory; -import org.apache.log4j.spi.LoggerRepository; -import org.apache.log4j.spi.OptionHandler; -import org.apache.log4j.spi.RendererSupport; -import org.apache.log4j.spi.ThrowableRenderer; -import org.apache.log4j.spi.ThrowableRendererSupport; -import org.apache.log4j.spi.ErrorHandler; - -/** - Allows the configuration of log4j from an external file. See - {@link #doConfigure(String, LoggerRepository)} for the - expected format. - -

It is sometimes useful to see how log4j is reading configuration - files. You can enable log4j internal logging by defining the - log4j.debug variable. - -

As of log4j version 0.8.5, at class initialization time class, - the file log4j.properties will be searched from the search - path used to load classes. If the file can be found, then it will - be fed to the {@link PropertyConfigurator#configure(java.net.URL)} - method. - -

The PropertyConfigurator does not handle the - advanced configuration features supported by the {@link - org.apache.log4j.xml.DOMConfigurator DOMConfigurator} such as - support custom {@link org.apache.log4j.spi.ErrorHandler ErrorHandlers}, - nested appenders such as the {@link org.apache.log4j.AsyncAppender - AsyncAppender}, etc. - -

All option values admit variable substitution. The - syntax of variable substitution is similar to that of Unix - shells. The string between an opening "${" and - closing "}" is interpreted as a key. The value of - the substituted variable can be defined as a system property or in - the configuration file itself. The value of the key is first - searched in the system properties, and if not found there, it is - then searched in the configuration file being parsed. The - corresponding value replaces the ${variableName} sequence. For - example, if java.home system property is set to - /home/xyz, then every occurrence of the sequence - ${java.home} will be interpreted as - /home/xyz. - - - @author Ceki Gülcü - @author Anders Kristensen - @since 0.8.1 */ -public class PropertyConfigurator implements Configurator { - - /** - Used internally to keep track of configured appenders. - */ - protected Hashtable registry = new Hashtable(11); - private LoggerRepository repository; - protected LoggerFactory loggerFactory = new DefaultCategoryFactory(); - - static final String CATEGORY_PREFIX = "log4j.category."; - static final String LOGGER_PREFIX = "log4j.logger."; - static final String FACTORY_PREFIX = "log4j.factory"; - static final String ADDITIVITY_PREFIX = "log4j.additivity."; - static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory"; - static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger"; - static final String APPENDER_PREFIX = "log4j.appender."; - static final String RENDERER_PREFIX = "log4j.renderer."; - static final String THRESHOLD_PREFIX = "log4j.threshold"; - private static final String THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer"; - private static final String LOGGER_REF = "logger-ref"; - private static final String ROOT_REF = "root-ref"; - private static final String APPENDER_REF_TAG = "appender-ref"; - - - /** Key for specifying the {@link org.apache.log4j.spi.LoggerFactory - LoggerFactory}. Currently set to "log4j.loggerFactory". */ - public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory"; - - /** - * If property set to true, then hierarchy will be reset before configuration. - */ - private static final String RESET_KEY = "log4j.reset"; - - static final private String INTERNAL_ROOT_NAME = "root"; - - /** - Read configuration from a file. The existing configuration is - not cleared nor reset. If you require a different behavior, - then call {@link LogManager#resetConfiguration - resetConfiguration} method before calling - doConfigure. - -

The configuration file consists of statements in the format - key=value. The syntax of different configuration - elements are discussed below. - -

Repository-wide threshold

- -

The repository-wide threshold filters logging requests by level - regardless of logger. The syntax is: - -

-    log4j.threshold=[level]
-    
- -

The level value can consist of the string values OFF, FATAL, - ERROR, WARN, INFO, DEBUG, ALL or a custom level value. A - custom level value can be specified in the form - level#classname. By default the repository-wide threshold is set - to the lowest possible value, namely the level ALL. -

- - -

Appender configuration

- -

Appender configuration syntax is: -

-    # For appender named appenderName, set its class.
-    # Note: The appender name can contain dots.
-    log4j.appender.appenderName=fully.qualified.name.of.appender.class
-
-    # Set appender specific options.
-    log4j.appender.appenderName.option1=value1
-    ...
-    log4j.appender.appenderName.optionN=valueN
-    
- - For each named appender you can configure its {@link Layout}. The - syntax for configuring an appender's layout is: -
-    log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class
-    log4j.appender.appenderName.layout.option1=value1
-    ....
-    log4j.appender.appenderName.layout.optionN=valueN
-    
- - The syntax for adding {@link Filter}s to an appender is: -
-    log4j.appender.appenderName.filter.ID=fully.qualified.name.of.filter.class
-    log4j.appender.appenderName.filter.ID.option1=value1
-    ...
-    log4j.appender.appenderName.filter.ID.optionN=valueN
-    
- The first line defines the class name of the filter identified by ID; - subsequent lines with the same ID specify filter option - value - paris. Multiple filters are added to the appender in the lexicographic - order of IDs. - - The syntax for adding an {@link ErrorHandler} to an appender is: -
-    log4j.appender.appenderName.errorhandler=fully.qualified.name.of.filter.class
-    log4j.appender.appenderName.errorhandler.root-ref={true|false}
-    log4j.appender.appenderName.errorhandler.logger-ref=loggerName
-    log4j.appender.appenderName.errorhandler.appender-ref=appenderName
-    log4j.appender.appenderName.errorhandler.option1=value1
-    ...
-    log4j.appender.appenderName.errorhandler.optionN=valueN
-    
- -

Configuring loggers

- -

The syntax for configuring the root logger is: -

-      log4j.rootLogger=[level], appenderName, appenderName, ...
-    
- -

This syntax means that an optional level can be - supplied followed by appender names separated by commas. - -

The level value can consist of the string values OFF, FATAL, - ERROR, WARN, INFO, DEBUG, ALL or a custom level value. A - custom level value can be specified in the form - level#classname. - -

If a level value is specified, then the root level is set - to the corresponding level. If no level value is specified, - then the root level remains untouched. - -

The root logger can be assigned multiple appenders. - -

Each appenderName (separated by commas) will be added to - the root logger. The named appender is defined using the - appender syntax defined above. - -

For non-root categories the syntax is almost the same: -

-    log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
-    
- -

The meaning of the optional level value is discussed above - in relation to the root logger. In addition however, the value - INHERITED can be specified meaning that the named logger should - inherit its level from the logger hierarchy. - -

If no level value is supplied, then the level of the - named logger remains untouched. - -

By default categories inherit their level from the - hierarchy. However, if you set the level of a logger and later - decide that that logger should inherit its level, then you should - specify INHERITED as the value for the level value. NULL is a - synonym for INHERITED. - -

Similar to the root logger syntax, each appenderName - (separated by commas) will be attached to the named logger. - -

See the appender - additivity rule in the user manual for the meaning of the - additivity flag. - -

ObjectRenderers

- - You can customize the way message objects of a given type are - converted to String before being logged. This is done by - specifying an {@link org.apache.log4j.or.ObjectRenderer ObjectRenderer} - for the object type would like to customize. - -

The syntax is: - -

-    log4j.renderer.fully.qualified.name.of.rendered.class=fully.qualified.name.of.rendering.class
-    
- - As in, -
-    log4j.renderer.my.Fruit=my.FruitRenderer
-    
- -

ThrowableRenderer

- - You can customize the way an instance of Throwable is - converted to String before being logged. This is done by - specifying an {@link org.apache.log4j.spi.ThrowableRenderer ThrowableRenderer}. - -

The syntax is: - -

-   log4j.throwableRenderer=fully.qualified.name.of.rendering.class
-   log4j.throwableRenderer.paramName=paramValue
-   
- - As in, -
-   log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer
-   
- -

Logger Factories

- - The usage of custom logger factories is discouraged and no longer - documented. - -

Resetting Hierarchy

- - The hierarchy will be reset before configuration when - log4j.reset=true is present in the properties file. - -

Example

- -

An example configuration is given below. Other configuration - file examples are given in the examples folder. - -

-
-    # Set options for appender named "A1".
-    # Appender "A1" will be a SyslogAppender
-    log4j.appender.A1=org.apache.log4j.net.SyslogAppender
-
-    # The syslog daemon resides on www.abc.net
-    log4j.appender.A1.SyslogHost=www.abc.net
-
-    # A1's layout is a PatternLayout, using the conversion pattern
-    # %r %-5p %c{2} %M.%L %x - %m\n. Thus, the log output will
-    # include # the relative time since the start of the application in
-    # milliseconds, followed by the level of the log request,
-    # followed by the two rightmost components of the logger name,
-    # followed by the callers method name, followed by the line number,
-    # the nested disgnostic context and finally the message itself.
-    # Refer to the documentation of {@link PatternLayout} for further information
-    # on the syntax of the ConversionPattern key.
-    log4j.appender.A1.layout=org.apache.log4j.PatternLayout
-    log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2} %M.%L %x - %m\n
-
-    # Set options for appender named "A2"
-    # A2 should be a RollingFileAppender, with maximum file size of 10 MB
-    # using at most one backup file. A2's layout is TTCC, using the
-    # ISO8061 date format with context printing enabled.
-    log4j.appender.A2=org.apache.log4j.RollingFileAppender
-    log4j.appender.A2.MaxFileSize=10MB
-    log4j.appender.A2.MaxBackupIndex=1
-    log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
-    log4j.appender.A2.layout.ContextPrinting=enabled
-    log4j.appender.A2.layout.DateFormat=ISO8601
-
-    # Root logger set to DEBUG using the A2 appender defined above.
-    log4j.rootLogger=DEBUG, A2
-
-    # Logger definitions:
-    # The SECURITY logger inherits is level from root. However, it's output
-    # will go to A1 appender defined above. It's additivity is non-cumulative.
-    log4j.logger.SECURITY=INHERIT, A1
-    log4j.additivity.SECURITY=false
-
-    # Only warnings or above will be logged for the logger "SECURITY.access".
-    # Output will go to A1.
-    log4j.logger.SECURITY.access=WARN
-
-
-    # The logger "class.of.the.day" inherits its level from the
-    # logger hierarchy.  Output will go to the appender's of the root
-    # logger, A2 in this case.
-    log4j.logger.class.of.the.day=INHERIT
-    
- -

Refer to the setOption method in each Appender and - Layout for class specific options. - -

Use the # or ! characters at the - beginning of a line for comments. - -

- @param configFileName The name of the configuration file where the - configuration information is stored. - - */ - public - void doConfigure(String configFileName, LoggerRepository hierarchy) { - Properties props = new Properties(); - FileInputStream istream = null; - try { - istream = new FileInputStream(configFileName); - props.load(istream); - istream.close(); - } - catch (Exception e) { - if (e instanceof InterruptedIOException || e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - LogLog.error("Could not read configuration file ["+configFileName+"].", e); - LogLog.error("Ignoring configuration file [" + configFileName+"]."); - return; - } finally { - if(istream != null) { - try { - istream.close(); - } catch(InterruptedIOException ignore) { - Thread.currentThread().interrupt(); - } catch(Throwable ignore) { - } - - } - } - // If we reach here, then the config file is alright. - doConfigure(props, hierarchy); - } - - /** - */ - static - public - void configure(String configFilename) { - new PropertyConfigurator().doConfigure(configFilename, - LogManager.getLoggerRepository()); - } - - /** - Read configuration options from url configURL. - - @since 0.8.2 - */ - public - static - void configure(java.net.URL configURL) { - new PropertyConfigurator().doConfigure(configURL, - LogManager.getLoggerRepository()); - } - - - /** - Read configuration options from properties. - - See {@link #doConfigure(String, LoggerRepository)} for the expected format. - */ - static - public - void configure(Properties properties) { - new PropertyConfigurator().doConfigure(properties, - LogManager.getLoggerRepository()); - } - - /** - Like {@link #configureAndWatch(String, long)} except that the - default delay as defined by {@link FileWatchdog#DEFAULT_DELAY} is - used. - - @param configFilename A file in key=value format. - - */ - static - public - void configureAndWatch(String configFilename) { - configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY); - } - - - /** - Read the configuration file configFilename if it - exists. Moreover, a thread will be created that will periodically - check if configFilename has been created or - modified. The period is determined by the delay - argument. If a change or file creation is detected, then - configFilename is read to configure log4j. - - @param configFilename A file in key=value format. - @param delay The delay in milliseconds to wait between each check. - */ - static - public - void configureAndWatch(String configFilename, long delay) { - PropertyWatchdog pdog = new PropertyWatchdog(configFilename); - pdog.setDelay(delay); - pdog.start(); - } - - - /** - Read configuration options from properties. - - See {@link #doConfigure(String, LoggerRepository)} for the expected format. - */ - public - void doConfigure(Properties properties, LoggerRepository hierarchy) { - repository = hierarchy; - String value = properties.getProperty(LogLog.DEBUG_KEY); - if(value == null) { - value = properties.getProperty("log4j.configDebug"); - if(value != null) - LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead."); - } - - if(value != null) { - LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true)); - } - - // - // if log4j.reset=true then - // reset hierarchy - String reset = properties.getProperty(RESET_KEY); - if (reset != null && OptionConverter.toBoolean(reset, false)) { - hierarchy.resetConfiguration(); - } - - String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX, - properties); - if(thresholdStr != null) { - hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr, - (Level) Level.ALL)); - LogLog.debug("Hierarchy threshold set to ["+hierarchy.getThreshold()+"]."); - } - - configureRootCategory(properties, hierarchy); - configureLoggerFactory(properties); - parseCatsAndRenderers(properties, hierarchy); - - LogLog.debug("Finished configuring."); - // We don't want to hold references to appenders preventing their - // garbage collection. - registry.clear(); - } - - /** - Read configuration options from url configURL. - */ - public - void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) { - Properties props = new Properties(); - LogLog.debug("Reading configuration from URL " + configURL); - InputStream istream = null; - URLConnection uConn = null; - try { - uConn = configURL.openConnection(); - uConn.setUseCaches(false); - istream = uConn.getInputStream(); - props.load(istream); - } - catch (Exception e) { - if (e instanceof InterruptedIOException || e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - LogLog.error("Could not read configuration file from URL [" + configURL - + "].", e); - LogLog.error("Ignoring configuration file [" + configURL +"]."); - return; - } - finally { - if (istream != null) { - try { - istream.close(); - } catch(InterruptedIOException ignore) { - Thread.currentThread().interrupt(); - } catch(IOException ignore) { - } catch(RuntimeException ignore) { - } - } - } - doConfigure(props, hierarchy); - } - - - // -------------------------------------------------------------------------- - // Internal stuff - // -------------------------------------------------------------------------- - - /** - Check the provided Properties object for a - {@link org.apache.log4j.spi.LoggerFactory LoggerFactory} - entry specified by {@link #LOGGER_FACTORY_KEY}. If such an entry - exists, an attempt is made to create an instance using the default - constructor. This instance is used for subsequent Category creations - within this configurator. - - @see #parseCatsAndRenderers - */ - protected void configureLoggerFactory(Properties props) { - String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY, - props); - if(factoryClassName != null) { - LogLog.debug("Setting category factory to ["+factoryClassName+"]."); - loggerFactory = (LoggerFactory) - OptionConverter.instantiateByClassName(factoryClassName, - LoggerFactory.class, - loggerFactory); - PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX + "."); - } - } - - /* - void configureOptionHandler(OptionHandler oh, String prefix, - Properties props) { - String[] options = oh.getOptionStrings(); - if(options == null) - return; - - String value; - for(int i = 0; i < options.length; i++) { - value = OptionConverter.findAndSubst(prefix + options[i], props); - LogLog.debug( - "Option " + options[i] + "=[" + (value == null? "N/A" : value)+"]."); - // Some option handlers assume that null value are not passed to them. - // So don't remove this check - if(value != null) { - oh.setOption(options[i], value); - } - } - oh.activateOptions(); - } - */ - - - void configureRootCategory(Properties props, LoggerRepository hierarchy) { - String effectiveFrefix = ROOT_LOGGER_PREFIX; - String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props); - - if(value == null) { - value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props); - effectiveFrefix = ROOT_CATEGORY_PREFIX; - } - - if(value == null) - LogLog.debug("Could not find root logger information. Is this OK?"); - else { - Logger root = hierarchy.getRootLogger(); - synchronized(root) { - parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value); - } - } - } - - - /** - Parse non-root elements, such non-root categories and renderers. - */ - protected - void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) { - Enumeration enumeration = props.propertyNames(); - while(enumeration.hasMoreElements()) { - String key = (String) enumeration.nextElement(); - if(key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) { - String loggerName = null; - if(key.startsWith(CATEGORY_PREFIX)) { - loggerName = key.substring(CATEGORY_PREFIX.length()); - } else if(key.startsWith(LOGGER_PREFIX)) { - loggerName = key.substring(LOGGER_PREFIX.length()); - } - String value = OptionConverter.findAndSubst(key, props); - Logger logger = hierarchy.getLogger(loggerName, loggerFactory); - synchronized(logger) { - parseCategory(props, logger, key, loggerName, value); - parseAdditivityForLogger(props, logger, loggerName); - } - } else if(key.startsWith(RENDERER_PREFIX)) { - String renderedClass = key.substring(RENDERER_PREFIX.length()); - String renderingClass = OptionConverter.findAndSubst(key, props); - if(hierarchy instanceof RendererSupport) { - RendererMap.addRenderer((RendererSupport) hierarchy, renderedClass, - renderingClass); - } - } else if (key.equals(THROWABLE_RENDERER_PREFIX)) { - if (hierarchy instanceof ThrowableRendererSupport) { - ThrowableRenderer tr = (ThrowableRenderer) - OptionConverter.instantiateByKey(props, - THROWABLE_RENDERER_PREFIX, - org.apache.log4j.spi.ThrowableRenderer.class, - null); - if(tr == null) { - LogLog.error( - "Could not instantiate throwableRenderer."); - } else { - PropertySetter setter = new PropertySetter(tr); - setter.setProperties(props, THROWABLE_RENDERER_PREFIX + "."); - ((ThrowableRendererSupport) hierarchy).setThrowableRenderer(tr); - - } - } - } - } - } - - /** - Parse the additivity option for a non-root category. - */ - void parseAdditivityForLogger(Properties props, Logger cat, - String loggerName) { - String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX + loggerName, - props); - LogLog.debug("Handling "+ADDITIVITY_PREFIX + loggerName+"=["+value+"]"); - // touch additivity only if necessary - if((value != null) && (!value.equals(""))) { - boolean additivity = OptionConverter.toBoolean(value, true); - LogLog.debug("Setting additivity for \""+loggerName+"\" to "+ - additivity); - cat.setAdditivity(additivity); - } - } - - /** - This method must work for the root category as well. - */ - void parseCategory(Properties props, Logger logger, String optionKey, - String loggerName, String value) { - - LogLog.debug("Parsing for [" +loggerName +"] with value=[" + value+"]."); - // We must skip over ',' but not white space - StringTokenizer st = new StringTokenizer(value, ","); - - // If value is not in the form ", appender.." or "", then we should set - // the level of the loggeregory. - - if(!(value.startsWith(",") || value.equals(""))) { - - // just to be on the safe side... - if(!st.hasMoreTokens()) - return; - - String levelStr = st.nextToken(); - LogLog.debug("Level token is [" + levelStr + "]."); - - // If the level value is inherited, set category level value to - // null. We also check that the user has not specified inherited for the - // root category. - if(INHERITED.equalsIgnoreCase(levelStr) || - NULL.equalsIgnoreCase(levelStr)) { - if(loggerName.equals(INTERNAL_ROOT_NAME)) { - LogLog.warn("The root logger cannot be set to null."); - } else { - logger.setLevel(null); - } - } else { - logger.setLevel(OptionConverter.toLevel(levelStr, (Level) Level.DEBUG)); - } - LogLog.debug("Category " + loggerName + " set to " + logger.getLevel()); - } - - // Begin by removing all existing appenders. - logger.removeAllAppenders(); - - Appender appender; - String appenderName; - while(st.hasMoreTokens()) { - appenderName = st.nextToken().trim(); - if(appenderName == null || appenderName.equals(",")) - continue; - LogLog.debug("Parsing appender named \"" + appenderName +"\"."); - appender = parseAppender(props, appenderName); - if(appender != null) { - logger.addAppender(appender); - } - } - } - - Appender parseAppender(Properties props, String appenderName) { - Appender appender = registryGet(appenderName); - if((appender != null)) { - LogLog.debug("Appender \"" + appenderName + "\" was already parsed."); - return appender; - } - // Appender was not previously initialized. - String prefix = APPENDER_PREFIX + appenderName; - String layoutPrefix = prefix + ".layout"; - - appender = (Appender) OptionConverter.instantiateByKey(props, prefix, - org.apache.log4j.Appender.class, - null); - if(appender == null) { - LogLog.error( - "Could not instantiate appender named \"" + appenderName+"\"."); - return null; - } - appender.setName(appenderName); - - if(appender instanceof OptionHandler) { - if(appender.requiresLayout()) { - Layout layout = (Layout) OptionConverter.instantiateByKey(props, - layoutPrefix, - Layout.class, - null); - if(layout != null) { - appender.setLayout(layout); - LogLog.debug("Parsing layout options for \"" + appenderName +"\"."); - //configureOptionHandler(layout, layoutPrefix + ".", props); - PropertySetter.setProperties(layout, props, layoutPrefix + "."); - LogLog.debug("End of parsing for \"" + appenderName +"\"."); - } - } - final String errorHandlerPrefix = prefix + ".errorhandler"; - String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props); - if (errorHandlerClass != null) { - ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByKey(props, - errorHandlerPrefix, - ErrorHandler.class, - null); - if (eh != null) { - appender.setErrorHandler(eh); - LogLog.debug("Parsing errorhandler options for \"" + appenderName +"\"."); - parseErrorHandler(eh, errorHandlerPrefix, props, repository); - final Properties edited = new Properties(); - final String[] keys = new String[] { - errorHandlerPrefix + "." + ROOT_REF, - errorHandlerPrefix + "." + LOGGER_REF, - errorHandlerPrefix + "." + APPENDER_REF_TAG - }; - for(Iterator iter = props.entrySet().iterator();iter.hasNext();) { - Map.Entry entry = (Map.Entry) iter.next(); - int i = 0; - for(; i < keys.length; i++) { - if(keys[i].equals(entry.getKey())) break; - } - if (i == keys.length) { - edited.put(entry.getKey(), entry.getValue()); - } - } - PropertySetter.setProperties(eh, edited, errorHandlerPrefix + "."); - LogLog.debug("End of errorhandler parsing for \"" + appenderName +"\"."); - } - - } - //configureOptionHandler((OptionHandler) appender, prefix + ".", props); - PropertySetter.setProperties(appender, props, prefix + "."); - LogLog.debug("Parsed \"" + appenderName +"\" options."); - } - parseAppenderFilters(props, appenderName, appender); - registryPut(appender); - return appender; - } - - private void parseErrorHandler( - final ErrorHandler eh, - final String errorHandlerPrefix, - final Properties props, - final LoggerRepository hierarchy) { - boolean rootRef = OptionConverter.toBoolean( - OptionConverter.findAndSubst(errorHandlerPrefix + ROOT_REF, props), false); - if (rootRef) { - eh.setLogger(hierarchy.getRootLogger()); - } - String loggerName = OptionConverter.findAndSubst(errorHandlerPrefix + LOGGER_REF , props); - if (loggerName != null) { - Logger logger = (loggerFactory == null) ? hierarchy.getLogger(loggerName) - : hierarchy.getLogger(loggerName, loggerFactory); - eh.setLogger(logger); - } - String appenderName = OptionConverter.findAndSubst(errorHandlerPrefix + APPENDER_REF_TAG, props); - if (appenderName != null) { - Appender backup = parseAppender(props, appenderName); - if (backup != null) { - eh.setBackupAppender(backup); - } - } - } - - - void parseAppenderFilters(Properties props, String appenderName, Appender appender) { - // extract filters and filter options from props into a hashtable mapping - // the property name defining the filter class to a list of pre-parsed - // name-value pairs associated to that filter - final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter."; - int fIdx = filterPrefix.length(); - Hashtable filters = new Hashtable(); - Enumeration e = props.keys(); - String name = ""; - while (e.hasMoreElements()) { - String key = (String) e.nextElement(); - if (key.startsWith(filterPrefix)) { - int dotIdx = key.indexOf('.', fIdx); - String filterKey = key; - if (dotIdx != -1) { - filterKey = key.substring(0, dotIdx); - name = key.substring(dotIdx+1); - } - Vector filterOpts = (Vector) filters.get(filterKey); - if (filterOpts == null) { - filterOpts = new Vector(); - filters.put(filterKey, filterOpts); - } - if (dotIdx != -1) { - String value = OptionConverter.findAndSubst(key, props); - filterOpts.add(new NameValue(name, value)); - } - } - } - - // sort filters by IDs, insantiate filters, set filter options, - // add filters to the appender - Enumeration g = new SortedKeyEnumeration(filters); - while (g.hasMoreElements()) { - String key = (String) g.nextElement(); - String clazz = props.getProperty(key); - if (clazz != null) { - LogLog.debug("Filter key: ["+key+"] class: ["+props.getProperty(key) +"] props: "+filters.get(key)); - Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, Filter.class, null); - if (filter != null) { - PropertySetter propSetter = new PropertySetter(filter); - Vector v = (Vector)filters.get(key); - Enumeration filterProps = v.elements(); - while (filterProps.hasMoreElements()) { - NameValue kv = (NameValue)filterProps.nextElement(); - propSetter.setProperty(kv.key, kv.value); - } - propSetter.activate(); - LogLog.debug("Adding filter of type ["+filter.getClass() - +"] to appender named ["+appender.getName()+"]."); - appender.addFilter(filter); - } - } else { - LogLog.warn("Missing class definition for filter: ["+key+"]"); - } - } - } - - - void registryPut(Appender appender) { - registry.put(appender.getName(), appender); - } - - Appender registryGet(String name) { - return (Appender) registry.get(name); - } -} - -class PropertyWatchdog extends FileWatchdog { - - PropertyWatchdog(String filename) { - super(filename); - } - - /** - Call {@link PropertyConfigurator#configure(String)} with the - filename to reconfigure log4j. */ - public - void doOnChange() { - new PropertyConfigurator().doConfigure(filename, - LogManager.getLoggerRepository()); - } -} - -class NameValue { - String key, value; - public NameValue(String key, String value) { - this.key = key; - this.value = value; - } - public String toString() { - return key + "=" + value; - } -} - -class SortedKeyEnumeration implements Enumeration { - - private Enumeration e; - - public SortedKeyEnumeration(Hashtable ht) { - Enumeration f = ht.keys(); - Vector keys = new Vector(ht.size()); - for (int i, last = 0; f.hasMoreElements(); ++last) { - String key = (String) f.nextElement(); - for (i = 0; i < last; ++i) { - String s = (String) keys.get(i); - if (key.compareTo(s) <= 0) break; - } - keys.add(i, key); - } - e = keys.elements(); - } - - public boolean hasMoreElements() { - return e.hasMoreElements(); - } - - public Object nextElement() { - return e.nextElement(); - } -} diff --git a/java/src/org/apache/log4j/ProvisionNode.java b/java/src/org/apache/log4j/ProvisionNode.java deleted file mode 100644 index f112682..0000000 --- a/java/src/org/apache/log4j/ProvisionNode.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import java.util.Vector; - -class ProvisionNode extends Vector { - private static final long serialVersionUID = -4479121426311014469L; - - ProvisionNode(Logger logger) { - super(); - this.addElement(logger); - } -} diff --git a/java/src/org/apache/log4j/RollingFileAppender.java b/java/src/org/apache/log4j/RollingFileAppender.java deleted file mode 100644 index 8a7c656..0000000 --- a/java/src/org/apache/log4j/RollingFileAppender.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - - -package org.apache.log4j; - -import java.io.IOException; -import java.io.Writer; -import java.io.File; -import java.io.InterruptedIOException; - -import org.apache.log4j.helpers.OptionConverter; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.helpers.CountingQuietWriter; -import org.apache.log4j.spi.LoggingEvent; - -/** - RollingFileAppender extends FileAppender to backup the log files when - they reach a certain size. - - The log4j extras companion includes alternatives which should be considered - for new deployments and which are discussed in the documentation - for org.apache.log4j.rolling.RollingFileAppender. - - - @author Heinz Richter - @author Ceki Gülcü - -*/ -public class RollingFileAppender extends FileAppender { - - /** - The default maximum file size is 10MB. - */ - protected long maxFileSize = 10*1024*1024; - - /** - There is one backup file by default. - */ - protected int maxBackupIndex = 1; - - private long nextRollover = 0; - - /** - The default constructor simply calls its {@link - FileAppender#FileAppender parents constructor}. */ - public - RollingFileAppender() { - super(); - } - - /** - Instantiate a RollingFileAppender and open the file designated by - filename. The opened filename will become the ouput - destination for this appender. - -

If the append parameter is true, the file will be - appended to. Otherwise, the file desginated by - filename will be truncated before being opened. - */ - public - RollingFileAppender(Layout layout, String filename, boolean append) - throws IOException { - super(layout, filename, append); - } - - /** - Instantiate a FileAppender and open the file designated by - filename. The opened filename will become the output - destination for this appender. - -

The file will be appended to. */ - public - RollingFileAppender(Layout layout, String filename) throws IOException { - super(layout, filename); - } - - /** - Returns the value of the MaxBackupIndex option. - */ - public - int getMaxBackupIndex() { - return maxBackupIndex; - } - - /** - Get the maximum size that the output file is allowed to reach - before being rolled over to backup files. - - @since 1.1 - */ - public - long getMaximumFileSize() { - return maxFileSize; - } - - /** - Implements the usual roll over behaviour. - -

If MaxBackupIndex is positive, then files - {File.1, ..., File.MaxBackupIndex -1} - are renamed to {File.2, ..., - File.MaxBackupIndex}. Moreover, File is - renamed File.1 and closed. A new File is - created to receive further log output. - -

If MaxBackupIndex is equal to zero, then the - File is truncated with no backup files created. - - */ - public // synchronization not necessary since doAppend is alreasy synched - void rollOver() { - File target; - File file; - - if (qw != null) { - long size = ((CountingQuietWriter) qw).getCount(); - LogLog.debug("rolling over count=" + size); - // if operation fails, do not roll again until - // maxFileSize more bytes are written - nextRollover = size + maxFileSize; - } - LogLog.debug("maxBackupIndex="+maxBackupIndex); - - boolean renameSucceeded = true; - // If maxBackups <= 0, then there is no file renaming to be done. - if(maxBackupIndex > 0) { - // Delete the oldest file, to keep Windows happy. - file = new File(fileName + '.' + maxBackupIndex); - if (file.exists()) - renameSucceeded = file.delete(); - - // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2} - for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) { - file = new File(fileName + "." + i); - if (file.exists()) { - target = new File(fileName + '.' + (i + 1)); - LogLog.debug("Renaming file " + file + " to " + target); - renameSucceeded = file.renameTo(target); - } - } - - if(renameSucceeded) { - // Rename fileName to fileName.1 - target = new File(fileName + "." + 1); - - this.closeFile(); // keep windows happy. - - file = new File(fileName); - LogLog.debug("Renaming file " + file + " to " + target); - renameSucceeded = file.renameTo(target); - // - // if file rename failed, reopen file with append = true - // - if (!renameSucceeded) { - try { - this.setFile(fileName, true, bufferedIO, bufferSize); - } - catch(IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("setFile("+fileName+", true) call failed.", e); - } - } - } - } - - // - // if all renames were successful, then - // - if (renameSucceeded) { - try { - // This will also close the file. This is OK since multiple - // close operations are safe. - this.setFile(fileName, false, bufferedIO, bufferSize); - nextRollover = 0; - } - catch(IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("setFile("+fileName+", false) call failed.", e); - } - } - } - - public - synchronized - void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) - throws IOException { - super.setFile(fileName, append, this.bufferedIO, this.bufferSize); - if(append) { - File f = new File(fileName); - ((CountingQuietWriter) qw).setCount(f.length()); - } - } - - - /** - Set the maximum number of backup files to keep around. - -

The MaxBackupIndex option determines how many backup - files are kept before the oldest is erased. This option takes - a positive integer value. If set to zero, then there will be no - backup files and the log file will be truncated when it reaches - MaxFileSize. - */ - public - void setMaxBackupIndex(int maxBackups) { - this.maxBackupIndex = maxBackups; - } - - /** - Set the maximum size that the output file is allowed to reach - before being rolled over to backup files. - -

This method is equivalent to {@link #setMaxFileSize} except - that it is required for differentiating the setter taking a - long argument from the setter taking a - String argument by the JavaBeans {@link - java.beans.Introspector Introspector}. - - @see #setMaxFileSize(String) - */ - public - void setMaximumFileSize(long maxFileSize) { - this.maxFileSize = maxFileSize; - } - - - /** - Set the maximum size that the output file is allowed to reach - before being rolled over to backup files. - -

In configuration files, the MaxFileSize option takes an - long integer in the range 0 - 2^63. You can specify the value - with the suffixes "KB", "MB" or "GB" so that the integer is - interpreted being expressed respectively in kilobytes, megabytes - or gigabytes. For example, the value "10KB" will be interpreted - as 10240. - */ - public - void setMaxFileSize(String value) { - maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1); - } - - protected - void setQWForFiles(Writer writer) { - this.qw = new CountingQuietWriter(writer, errorHandler); - } - - /** - This method differentiates RollingFileAppender from its super - class. - - @since 0.9.0 - */ - protected - void subAppend(LoggingEvent event) { - super.subAppend(event); - if(fileName != null && qw != null) { - long size = ((CountingQuietWriter) qw).getCount(); - if (size >= maxFileSize && size >= nextRollover) { - rollOver(); - } - } - } -} diff --git a/java/src/org/apache/log4j/SimpleLayout.java b/java/src/org/apache/log4j/SimpleLayout.java deleted file mode 100644 index 5699661..0000000 --- a/java/src/org/apache/log4j/SimpleLayout.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import org.apache.log4j.spi.LoggingEvent; - -/** - SimpleLayout consists of the level of the log statement, - followed by " - " and then the log message itself. For example, - -

-           DEBUG - Hello world
-   
- -

- @author Ceki Gülcü - @since version 0.7.0 - -

{@link PatternLayout} offers a much more powerful alternative. -*/ -public class SimpleLayout extends Layout { - - StringBuffer sbuf = new StringBuffer(128); - - public SimpleLayout() { - } - - public - void activateOptions() { - } - - /** - Returns the log statement in a format consisting of the - level, followed by " - " and then the - message. For example,

 INFO - "A message"
-     
- -

The category parameter is ignored. -

- @return A byte array in SimpleLayout format. - */ - public - String format(LoggingEvent event) { - - sbuf.setLength(0); - sbuf.append(event.getLevel().toString()); - sbuf.append(" - "); - sbuf.append(event.getRenderedMessage()); - sbuf.append(LINE_SEP); - return sbuf.toString(); - } - -/** - The SimpleLayout does not handle the throwable contained within - {@link LoggingEvent LoggingEvents}. Thus, it returns - true. - - @since version 0.8.4 */ - public - boolean ignoresThrowable() { - return true; - } -} diff --git a/java/src/org/apache/log4j/TTCCLayout.java b/java/src/org/apache/log4j/TTCCLayout.java deleted file mode 100644 index 3b0e98f..0000000 --- a/java/src/org/apache/log4j/TTCCLayout.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contributors: Christopher Williams -// Mathias Bogaert - -package org.apache.log4j; - -import org.apache.log4j.helpers.DateLayout; -import org.apache.log4j.spi.LoggingEvent; - -/** - TTCC layout format consists of time, thread, category and nested - diagnostic context information, hence the name. - -

Each of the four fields can be individually enabled or - disabled. The time format depends on the DateFormat - used. - -

Here is an example TTCCLayout output with the - {@link org.apache.log4j.helpers.RelativeTimeDateFormat}. - -

-176 [main] INFO  org.apache.log4j.examples.Sort - Populating an array of 2 elements in reverse order.
-225 [main] INFO  org.apache.log4j.examples.SortAlgo - Entered the sort method.
-262 [main] DEBUG org.apache.log4j.examples.SortAlgo.OUTER i=1 - Outer loop.
-276 [main] DEBUG org.apache.log4j.examples.SortAlgo.SWAP i=1 j=0 - Swapping intArray[0] = 1 and intArray[1] = 0
-290 [main] DEBUG org.apache.log4j.examples.SortAlgo.OUTER i=0 - Outer loop.
-304 [main] INFO  org.apache.log4j.examples.SortAlgo.DUMP - Dump of interger array:
-317 [main] INFO  org.apache.log4j.examples.SortAlgo.DUMP - Element [0] = 0
-331 [main] INFO  org.apache.log4j.examples.SortAlgo.DUMP - Element [1] = 1
-343 [main] INFO  org.apache.log4j.examples.Sort - The next log statement should be an error message.
-346 [main] ERROR org.apache.log4j.examples.SortAlgo.DUMP - Tried to dump an uninitialized array.
-        at org.apache.log4j.examples.SortAlgo.dump(SortAlgo.java:58)
-        at org.apache.log4j.examples.Sort.main(Sort.java:64)
-467 [main] INFO  org.apache.log4j.examples.Sort - Exiting main method.
-
- -

The first field is the number of milliseconds elapsed since the - start of the program. The second field is the thread outputting the - log statement. The third field is the level, the fourth field is - the category to which the statement belongs. - -

The fifth field (just before the '-') is the nested diagnostic - context. Note the nested diagnostic context may be empty as in the - first two statements. The text after the '-' is the message of the - statement. - -

WARNING Do not use the same TTCCLayout instance from - within different appenders. The TTCCLayout is not thread safe when - used in his way. However, it is perfectly safe to use a TTCCLayout - instance from just one appender. - -

{@link PatternLayout} offers a much more flexible alternative. - - @author Ceki Gülcü - @author Heinz Richter - -*/ -public class TTCCLayout extends DateLayout { - - // Internal representation of options - private boolean threadPrinting = true; - private boolean categoryPrefixing = true; - private boolean contextPrinting = true; - - - protected final StringBuffer buf = new StringBuffer(256); - - - /** - Instantiate a TTCCLayout object with {@link - org.apache.log4j.helpers.RelativeTimeDateFormat} as the date - formatter in the local time zone. - - @since 0.7.5 */ - public TTCCLayout() { - this.setDateFormat(RELATIVE_TIME_DATE_FORMAT, null); - } - - - /** - Instantiate a TTCCLayout object using the local time zone. The - DateFormat used will depend on the dateFormatType. - -

This constructor just calls the {@link - DateLayout#setDateFormat} method. - - */ - public TTCCLayout(String dateFormatType) { - this.setDateFormat(dateFormatType); - } - - - /** - The ThreadPrinting option specifies whether the name of the - current thread is part of log output or not. This is true by default. - */ - public - void setThreadPrinting(boolean threadPrinting) { - this.threadPrinting = threadPrinting; - } - - /** - Returns value of the ThreadPrinting option. - */ - public - boolean getThreadPrinting() { - return threadPrinting; - } - - /** - The CategoryPrefixing option specifies whether {@link Category} - name is part of log output or not. This is true by default. - */ - public - void setCategoryPrefixing(boolean categoryPrefixing) { - this.categoryPrefixing = categoryPrefixing; - } - - /** - Returns value of the CategoryPrefixing option. - */ - public - boolean getCategoryPrefixing() { - return categoryPrefixing; - } - - /** - The ContextPrinting option specifies log output will include - the nested context information belonging to the current thread. - This is true by default. - */ - public - void setContextPrinting(boolean contextPrinting) { - this.contextPrinting = contextPrinting; - } - - /** - Returns value of the ContextPrinting option. - */ - public - boolean getContextPrinting() { - return contextPrinting; - } - - /** - In addition to the level of the statement and message, the - returned byte array includes time, thread, category and {@link NDC} - information. - -

Time, thread, category and diagnostic context are printed - depending on options. - - @param event The event to format - - */ - public - String format(LoggingEvent event) { - - // Reset buf - buf.setLength(0); - - dateFormat(buf, event); - - if(this.threadPrinting) { - buf.append('['); - buf.append(event.getThreadName()); - buf.append("] "); - } - buf.append(event.getLevel().toString()); - buf.append(' '); - - if(this.categoryPrefixing) { - buf.append(event.getLoggerName()); - buf.append(' '); - } - - if(this.contextPrinting) { - String ndc = event.getNDC(); - - if(ndc != null) { - buf.append(ndc); - buf.append(' '); - } - } - buf.append("- "); - buf.append(event.getRenderedMessage()); - buf.append(LINE_SEP); - return buf.toString(); - } - - /** - The TTCCLayout does not handle the throwable contained within - {@link LoggingEvent LoggingEvents}. Thus, it returns - true. - - @since version 0.8.4 */ - public - boolean ignoresThrowable() { - return true; - } -} diff --git a/java/src/org/apache/log4j/WriterAppender.java b/java/src/org/apache/log4j/WriterAppender.java deleted file mode 100644 index 981c02d..0000000 --- a/java/src/org/apache/log4j/WriterAppender.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j; - -import java.io.IOException; -import java.io.InterruptedIOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; - -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.helpers.QuietWriter; -import org.apache.log4j.spi.ErrorHandler; -import org.apache.log4j.spi.LoggingEvent; - -// Contibutors: Jens Uwe Pipka -// Ben Sandee - -/** - WriterAppender appends log events to a {@link java.io.Writer} or an - {@link java.io.OutputStream} depending on the user's choice. - - @author Ceki Gülcü - @since 1.1 */ -public class WriterAppender extends AppenderSkeleton { - - - /** - Immediate flush means that the underlying writer or output stream - will be flushed at the end of each append operation unless shouldFlush() - is overridden. Immediate - flush is slower but ensures that each append request is actually - written. If immediateFlush is set to - false, then there is a good chance that the last few - logs events are not actually written to persistent media if and - when the application crashes. - -

The immediateFlush variable is set to - true by default. - - */ - protected boolean immediateFlush = true; - - /** - The encoding to use when writing.

The - encoding variable is set to null by - default which results in the utilization of the system's default - encoding. */ - protected String encoding; - - /** - This is the {@link QuietWriter quietWriter} where we will write - to. - */ - protected QuietWriter qw; - - - /** - This default constructor does nothing. */ - public - WriterAppender() { - } - - /** - Instantiate a WriterAppender and set the output destination to a - new {@link OutputStreamWriter} initialized with os - as its {@link OutputStream}. */ - public - WriterAppender(Layout layout, OutputStream os) { - this(layout, new OutputStreamWriter(os)); - } - - /** - Instantiate a WriterAppender and set the output destination to - writer. - -

The writer must have been previously opened by - the user. */ - public - WriterAppender(Layout layout, Writer writer) { - this.layout = layout; - this.setWriter(writer); - } - - /** - If the ImmediateFlush option is set to - true, the appender will flush at the end of each - write. This is the default behavior. If the option is set to - false, then the underlying stream can defer writing - to physical medium to a later time. - -

Avoiding the flush operation at the end of each append results in - a performance gain of 10 to 20 percent. However, there is safety - tradeoff involved in skipping flushing. Indeed, when flushing is - skipped, then it is likely that the last few log events will not - be recorded on disk when the application exits. This is a high - price to pay even for a 20% performance gain. - */ - public - void setImmediateFlush(boolean value) { - immediateFlush = value; - } - - /** - Returns value of the ImmediateFlush option. - */ - public - boolean getImmediateFlush() { - return immediateFlush; - } - - /** - Does nothing. - */ - public - void activateOptions() { - } - - - /** - This method is called by the {@link AppenderSkeleton#doAppend} - method. - -

If the output stream exists and is writable then write a log - statement to the output stream. Otherwise, write a single warning - message to System.err. - -

The format of the output will depend on this appender's - layout. - - */ - public - void append(LoggingEvent event) { - - // Reminder: the nesting of calls is: - // - // doAppend() - // - check threshold - // - filter - // - append(); - // - checkEntryConditions(); - // - subAppend(); - - if(!checkEntryConditions()) { - return; - } - subAppend(event); - } - - /** - This method determines if there is a sense in attempting to append. - -

It checks whether there is a set output target and also if - there is a set layout. If these checks fail, then the boolean - value false is returned. */ - protected - boolean checkEntryConditions() { - if(this.closed) { - LogLog.warn("Not allowed to write to a closed appender."); - return false; - } - - if(this.qw == null) { - errorHandler.error("No output stream or file set for the appender named ["+ - name+"]."); - return false; - } - - if(this.layout == null) { - errorHandler.error("No layout set for the appender named ["+ name+"]."); - return false; - } - return true; - } - - - /** - Close this appender instance. The underlying stream or writer is - also closed. - -

Closed appenders cannot be reused. - - @see #setWriter - @since 0.8.4 */ - public - synchronized - void close() { - if(this.closed) - return; - this.closed = true; - writeFooter(); - reset(); - } - - /** - * Close the underlying {@link java.io.Writer}. - * */ - protected void closeWriter() { - if(qw != null) { - try { - qw.close(); - } catch(IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - // There is do need to invoke an error handler at this late - // stage. - LogLog.error("Could not close " + qw, e); - } - } - } - - /** - Returns an OutputStreamWriter when passed an OutputStream. The - encoding used will depend on the value of the - encoding property. If the encoding value is - specified incorrectly the writer will be opened using the default - system encoding (an error message will be printed to the loglog. */ - protected - OutputStreamWriter createWriter(OutputStream os) { - OutputStreamWriter retval = null; - - String enc = getEncoding(); - if(enc != null) { - try { - retval = new OutputStreamWriter(os, enc); - } catch(IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.warn("Error initializing output writer."); - LogLog.warn("Unsupported encoding?"); - } - } - if(retval == null) { - retval = new OutputStreamWriter(os); - } - return retval; - } - - public String getEncoding() { - return encoding; - } - - public void setEncoding(String value) { - encoding = value; - } - - - - - /** - Set the {@link ErrorHandler} for this WriterAppender and also the - underlying {@link QuietWriter} if any. */ - public synchronized void setErrorHandler(ErrorHandler eh) { - if(eh == null) { - LogLog.warn("You have tried to set a null error-handler."); - } else { - this.errorHandler = eh; - if(this.qw != null) { - this.qw.setErrorHandler(eh); - } - } - } - - /** -

Sets the Writer where the log output will go. The - specified Writer must be opened by the user and be - writable. - -

The java.io.Writer will be closed when the - appender instance is closed. - - -

WARNING: Logging to an unopened Writer will fail. -

- @param writer An already opened Writer. */ - public synchronized void setWriter(Writer writer) { - reset(); - this.qw = new QuietWriter(writer, errorHandler); - //this.tp = new TracerPrintWriter(qw); - writeHeader(); - } - - - /** - Actual writing occurs here. - -

Most subclasses of WriterAppender will need to - override this method. - - @since 0.9.0 */ - protected - void subAppend(LoggingEvent event) { - this.qw.write(this.layout.format(event)); - - if(layout.ignoresThrowable()) { - String[] s = event.getThrowableStrRep(); - if (s != null) { - int len = s.length; - for(int i = 0; i < len; i++) { - this.qw.write(s[i]); - this.qw.write(Layout.LINE_SEP); - } - } - } - - if(shouldFlush(event)) { - this.qw.flush(); - } - } - - - - /** - The WriterAppender requires a layout. Hence, this method returns - true. - */ - public - boolean requiresLayout() { - return true; - } - - /** - Clear internal references to the writer and other variables. - - Subclasses can override this method for an alternate closing - behavior. */ - protected - void reset() { - closeWriter(); - this.qw = null; - //this.tp = null; - } - - - /** - Write a footer as produced by the embedded layout's {@link - Layout#getFooter} method. */ - protected - void writeFooter() { - if(layout != null) { - String f = layout.getFooter(); - if(f != null && this.qw != null) { - this.qw.write(f); - this.qw.flush(); - } - } - } - - /** - Write a header as produced by the embedded layout's {@link - Layout#getHeader} method. */ - protected - void writeHeader() { - if(layout != null) { - String h = layout.getHeader(); - if(h != null && this.qw != null) - this.qw.write(h); - } - } - - /** - * Determines whether the writer should be flushed after - * this event is written. - * - * @since 1.2.16 - */ - protected boolean shouldFlush(final LoggingEvent event) { - return immediateFlush; - } -} diff --git a/java/src/org/apache/log4j/chainsaw/ControlPanel.java b/java/src/org/apache/log4j/chainsaw/ControlPanel.java deleted file mode 100644 index 53b0b1b..0000000 --- a/java/src/org/apache/log4j/chainsaw/ControlPanel.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.chainsaw; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import org.apache.log4j.Logger; -import org.apache.log4j.Priority; -import org.apache.log4j.Level; - -/** - * Represents the controls for filtering, pausing, exiting, etc. - * - * @author Oliver Burn - */ -class ControlPanel extends JPanel { - /** use the log messages **/ - private static final Logger LOG = - Logger.getLogger(ControlPanel.class); - - /** - * Creates a new ControlPanel instance. - * - * @param aModel the model to control - */ - ControlPanel(final MyTableModel aModel) { - setBorder(BorderFactory.createTitledBorder("Controls: ")); - final GridBagLayout gridbag = new GridBagLayout(); - final GridBagConstraints c = new GridBagConstraints(); - setLayout(gridbag); - - // Pad everything - c.ipadx = 5; - c.ipady = 5; - - // Add the 1st column of labels - c.gridx = 0; - c.anchor = GridBagConstraints.EAST; - - c.gridy = 0; - JLabel label = new JLabel("Filter Level:"); - gridbag.setConstraints(label, c); - add(label); - - c.gridy++; - label = new JLabel("Filter Thread:"); - gridbag.setConstraints(label, c); - add(label); - - c.gridy++; - label = new JLabel("Filter Logger:"); - gridbag.setConstraints(label, c); - add(label); - - c.gridy++; - label = new JLabel("Filter NDC:"); - gridbag.setConstraints(label, c); - add(label); - - c.gridy++; - label = new JLabel("Filter Message:"); - gridbag.setConstraints(label, c); - add(label); - - // Add the 2nd column of filters - c.weightx = 1; - //c.weighty = 1; - c.gridx = 1; - c.anchor = GridBagConstraints.WEST; - - c.gridy = 0; - final Level[] allPriorities = new Level[] {Level.FATAL, - Level.ERROR, - Level.WARN, - Level.INFO, - Level.DEBUG, - Level.TRACE }; - - final JComboBox priorities = new JComboBox(allPriorities); - final Level lowest = allPriorities[allPriorities.length - 1]; - priorities.setSelectedItem(lowest); - aModel.setPriorityFilter(lowest); - gridbag.setConstraints(priorities, c); - add(priorities); - priorities.setEditable(false); - priorities.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent aEvent) { - aModel.setPriorityFilter( - (Priority) priorities.getSelectedItem()); - } - }); - - - c.fill = GridBagConstraints.HORIZONTAL; - c.gridy++; - final JTextField threadField = new JTextField(""); - threadField.getDocument().addDocumentListener(new DocumentListener () { - public void insertUpdate(DocumentEvent aEvent) { - aModel.setThreadFilter(threadField.getText()); - } - public void removeUpdate(DocumentEvent aEvente) { - aModel.setThreadFilter(threadField.getText()); - } - public void changedUpdate(DocumentEvent aEvent) { - aModel.setThreadFilter(threadField.getText()); - } - }); - gridbag.setConstraints(threadField, c); - add(threadField); - - c.gridy++; - final JTextField catField = new JTextField(""); - catField.getDocument().addDocumentListener(new DocumentListener () { - public void insertUpdate(DocumentEvent aEvent) { - aModel.setCategoryFilter(catField.getText()); - } - public void removeUpdate(DocumentEvent aEvent) { - aModel.setCategoryFilter(catField.getText()); - } - public void changedUpdate(DocumentEvent aEvent) { - aModel.setCategoryFilter(catField.getText()); - } - }); - gridbag.setConstraints(catField, c); - add(catField); - - c.gridy++; - final JTextField ndcField = new JTextField(""); - ndcField.getDocument().addDocumentListener(new DocumentListener () { - public void insertUpdate(DocumentEvent aEvent) { - aModel.setNDCFilter(ndcField.getText()); - } - public void removeUpdate(DocumentEvent aEvent) { - aModel.setNDCFilter(ndcField.getText()); - } - public void changedUpdate(DocumentEvent aEvent) { - aModel.setNDCFilter(ndcField.getText()); - } - }); - gridbag.setConstraints(ndcField, c); - add(ndcField); - - c.gridy++; - final JTextField msgField = new JTextField(""); - msgField.getDocument().addDocumentListener(new DocumentListener () { - public void insertUpdate(DocumentEvent aEvent) { - aModel.setMessageFilter(msgField.getText()); - } - public void removeUpdate(DocumentEvent aEvent) { - aModel.setMessageFilter(msgField.getText()); - } - public void changedUpdate(DocumentEvent aEvent) { - aModel.setMessageFilter(msgField.getText()); - } - }); - - - gridbag.setConstraints(msgField, c); - add(msgField); - - // Add the 3rd column of buttons - c.weightx = 0; - c.fill = GridBagConstraints.HORIZONTAL; - c.anchor = GridBagConstraints.EAST; - c.gridx = 2; - - c.gridy = 0; - final JButton exitButton = new JButton("Exit"); - exitButton.setMnemonic('x'); - exitButton.addActionListener(ExitAction.INSTANCE); - gridbag.setConstraints(exitButton, c); - add(exitButton); - - c.gridy++; - final JButton clearButton = new JButton("Clear"); - clearButton.setMnemonic('c'); - clearButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent aEvent) { - aModel.clear(); - } - }); - gridbag.setConstraints(clearButton, c); - add(clearButton); - - c.gridy++; - final JButton toggleButton = new JButton("Pause"); - toggleButton.setMnemonic('p'); - toggleButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent aEvent) { - aModel.toggle(); - toggleButton.setText( - aModel.isPaused() ? "Resume" : "Pause"); - } - }); - gridbag.setConstraints(toggleButton, c); - add(toggleButton); - } -} diff --git a/java/src/org/apache/log4j/chainsaw/DetailPanel.java b/java/src/org/apache/log4j/chainsaw/DetailPanel.java deleted file mode 100644 index 1f5dfe2..0000000 --- a/java/src/org/apache/log4j/chainsaw/DetailPanel.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.chainsaw; - -import java.awt.BorderLayout; -import java.text.MessageFormat; -import java.util.Date; -import javax.swing.BorderFactory; -import javax.swing.JEditorPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import org.apache.log4j.Logger; - -/** - * A panel for showing a stack trace. - * - * @author Oliver Burn - */ -class DetailPanel - extends JPanel - implements ListSelectionListener -{ - /** used to log events **/ - private static final Logger LOG = - Logger.getLogger(DetailPanel.class); - - /** used to format the logging event **/ - private static final MessageFormat FORMATTER = new MessageFormat( - "Time: {0,time,medium}" + - "  Priority: {1}" + - "  Thread: {2}" + - "  NDC: {3}" + - "
Logger: {4}" + - "
Location: {5}" + - "
Message:" + - "

{6}
" + - "Throwable:" + - "
{7}
"); - - /** the model for the data to render **/ - private final MyTableModel mModel; - /** pane for rendering detail **/ - private final JEditorPane mDetails; - - /** - * Creates a new DetailPanel instance. - * - * @param aTable the table to listen for selections on - * @param aModel the model backing the table - */ - DetailPanel(JTable aTable, final MyTableModel aModel) { - mModel = aModel; - setLayout(new BorderLayout()); - setBorder(BorderFactory.createTitledBorder("Details: ")); - - mDetails = new JEditorPane(); - mDetails.setEditable(false); - mDetails.setContentType("text/html"); - add(new JScrollPane(mDetails), BorderLayout.CENTER); - - final ListSelectionModel rowSM = aTable.getSelectionModel(); - rowSM.addListSelectionListener(this); - } - - /** @see ListSelectionListener **/ - public void valueChanged(ListSelectionEvent aEvent) { - //Ignore extra messages. - if (aEvent.getValueIsAdjusting()) { - return; - } - - final ListSelectionModel lsm = (ListSelectionModel) aEvent.getSource(); - if (lsm.isSelectionEmpty()) { - mDetails.setText("Nothing selected"); - } else { - final int selectedRow = lsm.getMinSelectionIndex(); - final EventDetails e = mModel.getEventDetails(selectedRow); - final Object[] args = - { - new Date(e.getTimeStamp()), - e.getPriority(), - escape(e.getThreadName()), - escape(e.getNDC()), - escape(e.getCategoryName()), - escape(e.getLocationDetails()), - escape(e.getMessage()), - escape(getThrowableStrRep(e)) - }; - mDetails.setText(FORMATTER.format(args)); - mDetails.setCaretPosition(0); - } - } - - //////////////////////////////////////////////////////////////////////////// - // Private methods - //////////////////////////////////////////////////////////////////////////// - - /** - * Returns a string representation of a throwable. - * - * @param aEvent contains the throwable information - * @return a String value - */ - private static String getThrowableStrRep(EventDetails aEvent) { - final String[] strs = aEvent.getThrowableStrRep(); - if (strs == null) { - return null; - } - - final StringBuffer sb = new StringBuffer(); - for (int i = 0; i < strs.length; i++) { - sb.append(strs[i]).append("\n"); - } - - return sb.toString(); - } - - /** - * Escape <, > & and " as their entities. It is very - * dumb about & handling. - * @param aStr the String to escape. - * @return the escaped String - */ - private String escape(String aStr) { - if (aStr == null) { - return null; - } - - final StringBuffer buf = new StringBuffer(); - for (int i = 0; i < aStr.length(); i++) { - char c = aStr.charAt(i); - switch (c) { - case '<': - buf.append("<"); - break; - case '>': - buf.append(">"); - break; - case '\"': - buf.append("""); - break; - case '&': - buf.append("&"); - break; - default: - buf.append(c); - break; - } - } - return buf.toString(); - } -} diff --git a/java/src/org/apache/log4j/chainsaw/EventDetails.java b/java/src/org/apache/log4j/chainsaw/EventDetails.java deleted file mode 100644 index 4b3ad94..0000000 --- a/java/src/org/apache/log4j/chainsaw/EventDetails.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.chainsaw; - -import org.apache.log4j.Priority; -import org.apache.log4j.spi.LoggingEvent; - -/** - * Represents the details of a logging event. It is intended to overcome the - * problem that a LoggingEvent cannot be constructed with purely fake data. - * - * @author Oliver Burn - * @version 1.0 - */ -class EventDetails { - - /** the time of the event **/ - private final long mTimeStamp; - /** the priority of the event **/ - private final Priority mPriority; - /** the category of the event **/ - private final String mCategoryName; - /** the NDC for the event **/ - private final String mNDC; - /** the thread for the event **/ - private final String mThreadName; - /** the msg for the event **/ - private final String mMessage; - /** the throwable details the event **/ - private final String[] mThrowableStrRep; - /** the location details for the event **/ - private final String mLocationDetails; - - /** - * Creates a new EventDetails instance. - * @param aTimeStamp a long value - * @param aPriority a Priority value - * @param aCategoryName a String value - * @param aNDC a String value - * @param aThreadName a String value - * @param aMessage a String value - * @param aThrowableStrRep a String[] value - * @param aLocationDetails a String value - */ - EventDetails(long aTimeStamp, - Priority aPriority, - String aCategoryName, - String aNDC, - String aThreadName, - String aMessage, - String[] aThrowableStrRep, - String aLocationDetails) - { - mTimeStamp = aTimeStamp; - mPriority = aPriority; - mCategoryName = aCategoryName; - mNDC = aNDC; - mThreadName = aThreadName; - mMessage = aMessage; - mThrowableStrRep = aThrowableStrRep; - mLocationDetails = aLocationDetails; - } - - /** - * Creates a new EventDetails instance. - * - * @param aEvent a LoggingEvent value - */ - EventDetails(LoggingEvent aEvent) { - - this(aEvent.timeStamp, - aEvent.getLevel(), - aEvent.getLoggerName(), - aEvent.getNDC(), - aEvent.getThreadName(), - aEvent.getRenderedMessage(), - aEvent.getThrowableStrRep(), - (aEvent.getLocationInformation() == null) - ? null : aEvent.getLocationInformation().fullInfo); - } - - /** @see #mTimeStamp **/ - long getTimeStamp() { - return mTimeStamp; - } - - /** @see #mPriority **/ - Priority getPriority() { - return mPriority; - } - - /** @see #mCategoryName **/ - String getCategoryName() { - return mCategoryName; - } - - /** @see #mNDC **/ - String getNDC() { - return mNDC; - } - - /** @see #mThreadName **/ - String getThreadName() { - return mThreadName; - } - - /** @see #mMessage **/ - String getMessage() { - return mMessage; - } - - /** @see #mLocationDetails **/ - String getLocationDetails(){ - return mLocationDetails; - } - - /** @see #mThrowableStrRep **/ - String[] getThrowableStrRep() { - return mThrowableStrRep; - } -} diff --git a/java/src/org/apache/log4j/chainsaw/ExitAction.java b/java/src/org/apache/log4j/chainsaw/ExitAction.java deleted file mode 100644 index 55a100e..0000000 --- a/java/src/org/apache/log4j/chainsaw/ExitAction.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.chainsaw; - -import java.awt.event.ActionEvent; -import javax.swing.AbstractAction; -import org.apache.log4j.Logger; - -/** - * Encapsulates the action to exit. - * - * @author Oliver Burn - * @version 1.0 - */ -class ExitAction - extends AbstractAction -{ - /** use to log messages **/ - private static final Logger LOG = Logger.getLogger(ExitAction.class); - /** The instance to share **/ - public static final ExitAction INSTANCE = new ExitAction(); - - /** Stop people creating instances **/ - private ExitAction() {} - - /** - * Will shutdown the application. - * @param aIgnore ignored - */ - public void actionPerformed(ActionEvent aIgnore) { - LOG.info("shutting down"); - System.exit(0); - } -} diff --git a/java/src/org/apache/log4j/chainsaw/LoadXMLAction.java b/java/src/org/apache/log4j/chainsaw/LoadXMLAction.java deleted file mode 100644 index 33e5d13..0000000 --- a/java/src/org/apache/log4j/chainsaw/LoadXMLAction.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.chainsaw; - -import java.awt.event.ActionEvent; -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import javax.swing.AbstractAction; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParserFactory; -import org.apache.log4j.Logger; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; - -/** - * Encapsulates the action to load an XML file. - * - * @author Oliver Burn - * @version 1.0 - */ -class LoadXMLAction - extends AbstractAction -{ - /** use to log messages **/ - private static final Logger LOG = Logger.getLogger(LoadXMLAction.class); - - /** the parent frame **/ - private final JFrame mParent; - - /** - * the file chooser - configured to allow only the selection of a - * single file. - */ - private final JFileChooser mChooser = new JFileChooser(); - { - mChooser.setMultiSelectionEnabled(false); - mChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - } - - /** parser to read XML files **/ - private final XMLReader mParser; - /** the content handler **/ - private final XMLFileHandler mHandler; - - - /** - * Creates a new LoadXMLAction instance. - * - * @param aParent the parent frame - * @param aModel the model to add events to - * @exception SAXException if an error occurs - * @throws ParserConfigurationException if an error occurs - */ - LoadXMLAction(JFrame aParent, MyTableModel aModel) - throws SAXException, ParserConfigurationException - { - mParent = aParent; - mHandler = new XMLFileHandler(aModel); - mParser = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); - mParser.setContentHandler(mHandler); - } - - /** - * Prompts the user for a file to load events from. - * @param aIgnore an ActionEvent value - */ - public void actionPerformed(ActionEvent aIgnore) { - LOG.info("load file called"); - if (mChooser.showOpenDialog(mParent) == JFileChooser.APPROVE_OPTION) { - LOG.info("Need to load a file"); - final File chosen = mChooser.getSelectedFile(); - LOG.info("loading the contents of " + chosen.getAbsolutePath()); - try { - final int num = loadFile(chosen.getAbsolutePath()); - JOptionPane.showMessageDialog( - mParent, - "Loaded " + num + " events.", - "CHAINSAW", - JOptionPane.INFORMATION_MESSAGE); - } catch (Exception e) { - LOG.warn("caught an exception loading the file", e); - JOptionPane.showMessageDialog( - mParent, - "Error parsing file - " + e.getMessage(), - "CHAINSAW", - JOptionPane.ERROR_MESSAGE); - } - } - } - - /** - * Loads the contents of file into the model - * - * @param aFile the file to extract events from - * @return the number of events loaded - * @throws SAXException if an error occurs - * @throws IOException if an error occurs - */ - private int loadFile(String aFile) - throws SAXException, IOException - { - synchronized (mParser) { - // Create a dummy document to parse the file - final StringBuffer buf = new StringBuffer(); - buf.append("\n"); - buf.append("]>\n"); - buf.append("\n"); - buf.append("&data;\n"); - buf.append("\n"); - - final InputSource is = - new InputSource(new StringReader(buf.toString())); - mParser.parse(is); - return mHandler.getNumEvents(); - } - } -} diff --git a/java/src/org/apache/log4j/chainsaw/LoggingReceiver.java b/java/src/org/apache/log4j/chainsaw/LoggingReceiver.java deleted file mode 100644 index ca087ad..0000000 --- a/java/src/org/apache/log4j/chainsaw/LoggingReceiver.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.chainsaw; - -import java.io.EOFException; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import org.apache.log4j.Logger; -import org.apache.log4j.spi.LoggingEvent; - -/** - * A daemon thread the processes connections from a - * org.apache.log4j.net.SocketAppender.html. - * - * @author Oliver Burn - */ -class LoggingReceiver extends Thread { - /** used to log messages **/ - private static final Logger LOG = Logger.getLogger(LoggingReceiver.class); - - /** - * Helper that actually processes a client connection. It receives events - * and adds them to the supplied model. - * - * @author Oliver Burn - */ - private class Slurper implements Runnable { - /** socket connection to read events from **/ - private final Socket mClient; - - /** - * Creates a new Slurper instance. - * - * @param aClient socket to receive events from - */ - Slurper(Socket aClient) { - mClient = aClient; - } - - /** loops getting the events **/ - public void run() { - LOG.debug("Starting to get data"); - try { - final ObjectInputStream ois = - new ObjectInputStream(mClient.getInputStream()); - while (true) { - final LoggingEvent event = (LoggingEvent) ois.readObject(); - mModel.addEvent(new EventDetails(event)); - } - } catch (EOFException e) { - LOG.info("Reached EOF, closing connection"); - } catch (SocketException e) { - LOG.info("Caught SocketException, closing connection"); - } catch (IOException e) { - LOG.warn("Got IOException, closing connection", e); - } catch (ClassNotFoundException e) { - LOG.warn("Got ClassNotFoundException, closing connection", e); - } - - try { - mClient.close(); - } catch (IOException e) { - LOG.warn("Error closing connection", e); - } - } - } - - /** where to put the events **/ - private MyTableModel mModel; - - /** server for listening for connections **/ - private ServerSocket mSvrSock; - - /** - * Creates a new LoggingReceiver instance. - * - * @param aModel model to place put received into - * @param aPort port to listen on - * @throws IOException if an error occurs - */ - LoggingReceiver(MyTableModel aModel, int aPort) throws IOException { - setDaemon(true); - mModel = aModel; - mSvrSock = new ServerSocket(aPort); - } - - /** Listens for client connections **/ - public void run() { - LOG.info("Thread started"); - try { - while (true) { - LOG.debug("Waiting for a connection"); - final Socket client = mSvrSock.accept(); - LOG.debug("Got a connection from " + - client.getInetAddress().getHostName()); - final Thread t = new Thread(new Slurper(client)); - t.setDaemon(true); - t.start(); - } - } catch (IOException e) { - LOG.error("Error in accepting connections, stopping.", e); - } - } -} diff --git a/java/src/org/apache/log4j/chainsaw/Main.java b/java/src/org/apache/log4j/chainsaw/Main.java deleted file mode 100644 index 5a69351..0000000 --- a/java/src/org/apache/log4j/chainsaw/Main.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.chainsaw; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.IOException; -import java.util.Properties; -import javax.swing.BorderFactory; -import javax.swing.JFrame; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; - -/** - * The main application. - * - * @author Oliver Burn - */ -public class Main - extends JFrame -{ - /** the default port number to listen on **/ - private static final int DEFAULT_PORT = 4445; - - /** name of property for port name **/ - public static final String PORT_PROP_NAME = "chainsaw.port"; - - /** use to log messages **/ - private static final Logger LOG = Logger.getLogger(Main.class); - - - /** - * Creates a new NetworkClient instance. - */ - private Main() { - super("CHAINSAW - Log4J Log Viewer"); - // create the all important model - final MyTableModel model = new MyTableModel(); - - //Create the menu bar. - final JMenuBar menuBar = new JMenuBar(); - setJMenuBar(menuBar); - final JMenu menu = new JMenu("File"); - menuBar.add(menu); - - try { - final LoadXMLAction lxa = new LoadXMLAction(this, model); - final JMenuItem loadMenuItem = new JMenuItem("Load file..."); - menu.add(loadMenuItem); - loadMenuItem.addActionListener(lxa); - } catch (NoClassDefFoundError e) { - LOG.info("Missing classes for XML parser", e); - JOptionPane.showMessageDialog( - this, - "XML parser not in classpath - unable to load XML events.", - "CHAINSAW", - JOptionPane.ERROR_MESSAGE); - } catch (Exception e) { - LOG.info("Unable to create the action to load XML files", e); - JOptionPane.showMessageDialog( - this, - "Unable to create a XML parser - unable to load XML events.", - "CHAINSAW", - JOptionPane.ERROR_MESSAGE); - } - - final JMenuItem exitMenuItem = new JMenuItem("Exit"); - menu.add(exitMenuItem); - exitMenuItem.addActionListener(ExitAction.INSTANCE); - - // Add control panel - final ControlPanel cp = new ControlPanel(model); - getContentPane().add(cp, BorderLayout.NORTH); - - // Create the table - final JTable table = new JTable(model); - table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - final JScrollPane scrollPane = new JScrollPane(table); - scrollPane.setBorder(BorderFactory.createTitledBorder("Events: ")); - scrollPane.setPreferredSize(new Dimension(900, 300)); - - // Create the details - final JPanel details = new DetailPanel(table, model); - details.setPreferredSize(new Dimension(900, 300)); - - // Add the table and stack trace into a splitter - final JSplitPane jsp = - new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPane, details); - getContentPane().add(jsp, BorderLayout.CENTER); - - addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent aEvent) { - ExitAction.INSTANCE.actionPerformed(null); - } - }); - - pack(); - setVisible(true); - - setupReceiver(model); - } - - /** - * Setup recieving messages. - * - * @param aModel a MyTableModel value - */ - private void setupReceiver(MyTableModel aModel) { - int port = DEFAULT_PORT; - final String strRep = System.getProperty(PORT_PROP_NAME); - if (strRep != null) { - try { - port = Integer.parseInt(strRep); - } catch (NumberFormatException nfe) { - LOG.fatal("Unable to parse " + PORT_PROP_NAME + - " property with value " + strRep + "."); - JOptionPane.showMessageDialog( - this, - "Unable to parse port number from '" + strRep + - "', quitting.", - "CHAINSAW", - JOptionPane.ERROR_MESSAGE); - System.exit(1); - } - } - - try { - final LoggingReceiver lr = new LoggingReceiver(aModel, port); - lr.start(); - } catch (IOException e) { - LOG.fatal("Unable to connect to socket server, quiting", e); - JOptionPane.showMessageDialog( - this, - "Unable to create socket on port " + port + ", quitting.", - "CHAINSAW", - JOptionPane.ERROR_MESSAGE); - System.exit(1); - } - } - - - //////////////////////////////////////////////////////////////////////////// - // static methods - //////////////////////////////////////////////////////////////////////////// - - - /** initialise log4j **/ - private static void initLog4J() { - final Properties props = new Properties(); - props.setProperty("log4j.rootLogger", "DEBUG, A1"); - props.setProperty("log4j.appender.A1", - "org.apache.log4j.ConsoleAppender"); - props.setProperty("log4j.appender.A1.layout", - "org.apache.log4j.TTCCLayout"); - PropertyConfigurator.configure(props); - } - - /** - * The main method. - * - * @param aArgs ignored - */ - public static void main(String[] aArgs) { - initLog4J(); - new Main(); - } -} diff --git a/java/src/org/apache/log4j/chainsaw/MyTableModel.java b/java/src/org/apache/log4j/chainsaw/MyTableModel.java deleted file mode 100644 index 0d43272..0000000 --- a/java/src/org/apache/log4j/chainsaw/MyTableModel.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.chainsaw; - -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.SortedSet; -import java.util.TreeSet; -import javax.swing.table.AbstractTableModel; -import org.apache.log4j.Priority; -import org.apache.log4j.Logger; - -/** - * Represents a list of EventDetails objects that are sorted on - * logging time. Methods are provided to filter the events that are visible. - * - * @author Oliver Burn - */ -class MyTableModel - extends AbstractTableModel -{ - - /** used to log messages **/ - private static final Logger LOG = Logger.getLogger(MyTableModel.class); - - /** use the compare logging events **/ - private static final Comparator MY_COMP = new Comparator() - { - /** @see Comparator **/ - public int compare(Object aObj1, Object aObj2) { - if ((aObj1 == null) && (aObj2 == null)) { - return 0; // treat as equal - } else if (aObj1 == null) { - return -1; // null less than everything - } else if (aObj2 == null) { - return 1; // think about it. :-> - } - - // will assume only have LoggingEvent - final EventDetails le1 = (EventDetails) aObj1; - final EventDetails le2 = (EventDetails) aObj2; - - if (le1.getTimeStamp() < le2.getTimeStamp()) { - return 1; - } - // assume not two events are logged at exactly the same time - return -1; - } - }; - - /** - * Helper that actually processes incoming events. - * @author Oliver Burn - */ - private class Processor - implements Runnable - { - /** loops getting the events **/ - public void run() { - while (true) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // ignore - } - - synchronized (mLock) { - if (mPaused) { - continue; - } - - boolean toHead = true; // were events added to head - boolean needUpdate = false; - final Iterator it = mPendingEvents.iterator(); - while (it.hasNext()) { - final EventDetails event = (EventDetails) it.next(); - mAllEvents.add(event); - toHead = toHead && (event == mAllEvents.first()); - needUpdate = needUpdate || matchFilter(event); - } - mPendingEvents.clear(); - - if (needUpdate) { - updateFilteredEvents(toHead); - } - } - } - - } - } - - - /** names of the columns in the table **/ - private static final String[] COL_NAMES = { - "Time", "Priority", "Trace", "Category", "NDC", "Message"}; - - /** definition of an empty list **/ - private static final EventDetails[] EMPTY_LIST = new EventDetails[] {}; - - /** used to format dates **/ - private static final DateFormat DATE_FORMATTER = - DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM); - - /** the lock to control access **/ - private final Object mLock = new Object(); - /** set of all logged events - not filtered **/ - private final SortedSet mAllEvents = new TreeSet(MY_COMP); - /** events that are visible after filtering **/ - private EventDetails[] mFilteredEvents = EMPTY_LIST; - /** list of events that are buffered for processing **/ - private final List mPendingEvents = new ArrayList(); - /** indicates whether event collection is paused to the UI **/ - private boolean mPaused = false; - - /** filter for the thread **/ - private String mThreadFilter = ""; - /** filter for the message **/ - private String mMessageFilter = ""; - /** filter for the NDC **/ - private String mNDCFilter = ""; - /** filter for the category **/ - private String mCategoryFilter = ""; - /** filter for the priority **/ - private Priority mPriorityFilter = Priority.DEBUG; - - - /** - * Creates a new MyTableModel instance. - * - */ - MyTableModel() { - final Thread t = new Thread(new Processor()); - t.setDaemon(true); - t.start(); - } - - - //////////////////////////////////////////////////////////////////////////// - // Table Methods - //////////////////////////////////////////////////////////////////////////// - - /** @see javax.swing.table.TableModel **/ - public int getRowCount() { - synchronized (mLock) { - return mFilteredEvents.length; - } - } - - /** @see javax.swing.table.TableModel **/ - public int getColumnCount() { - // does not need to be synchronized - return COL_NAMES.length; - } - - /** @see javax.swing.table.TableModel **/ - public String getColumnName(int aCol) { - // does not need to be synchronized - return COL_NAMES[aCol]; - } - - /** @see javax.swing.table.TableModel **/ - public Class getColumnClass(int aCol) { - // does not need to be synchronized - return (aCol == 2) ? Boolean.class : Object.class; - } - - /** @see javax.swing.table.TableModel **/ - public Object getValueAt(int aRow, int aCol) { - synchronized (mLock) { - final EventDetails event = mFilteredEvents[aRow]; - - if (aCol == 0) { - return DATE_FORMATTER.format(new Date(event.getTimeStamp())); - } else if (aCol == 1) { - return event.getPriority(); - } else if (aCol == 2) { - return (event.getThrowableStrRep() == null) - ? Boolean.FALSE : Boolean.TRUE; - } else if (aCol == 3) { - return event.getCategoryName(); - } else if (aCol == 4) { - return event.getNDC(); - } - return event.getMessage(); - } - } - - //////////////////////////////////////////////////////////////////////////// - // Public Methods - //////////////////////////////////////////////////////////////////////////// - - /** - * Sets the priority to filter events on. Only events of equal or higher - * property are now displayed. - * - * @param aPriority the priority to filter on - */ - public void setPriorityFilter(Priority aPriority) { - synchronized (mLock) { - mPriorityFilter = aPriority; - updateFilteredEvents(false); - } - } - - /** - * Set the filter for the thread field. - * - * @param aStr the string to match - */ - public void setThreadFilter(String aStr) { - synchronized (mLock) { - mThreadFilter = aStr.trim(); - updateFilteredEvents(false); - } - } - - /** - * Set the filter for the message field. - * - * @param aStr the string to match - */ - public void setMessageFilter(String aStr) { - synchronized (mLock) { - mMessageFilter = aStr.trim(); - updateFilteredEvents(false); - } - } - - /** - * Set the filter for the NDC field. - * - * @param aStr the string to match - */ - public void setNDCFilter(String aStr) { - synchronized (mLock) { - mNDCFilter = aStr.trim(); - updateFilteredEvents(false); - } - } - - /** - * Set the filter for the category field. - * - * @param aStr the string to match - */ - public void setCategoryFilter(String aStr) { - synchronized (mLock) { - mCategoryFilter = aStr.trim(); - updateFilteredEvents(false); - } - } - - /** - * Add an event to the list. - * - * @param aEvent a EventDetails value - */ - public void addEvent(EventDetails aEvent) { - synchronized (mLock) { - mPendingEvents.add(aEvent); - } - } - - /** - * Clear the list of all events. - */ - public void clear() { - synchronized (mLock) { - mAllEvents.clear(); - mFilteredEvents = new EventDetails[0]; - mPendingEvents.clear(); - fireTableDataChanged(); - } - } - - /** Toggle whether collecting events **/ - public void toggle() { - synchronized (mLock) { - mPaused = !mPaused; - } - } - - /** @return whether currently paused collecting events **/ - public boolean isPaused() { - synchronized (mLock) { - return mPaused; - } - } - - /** - * Get the throwable information at a specified row in the filtered events. - * - * @param aRow the row index of the event - * @return the throwable information - */ - public EventDetails getEventDetails(int aRow) { - synchronized (mLock) { - return mFilteredEvents[aRow]; - } - } - - //////////////////////////////////////////////////////////////////////////// - // Private methods - //////////////////////////////////////////////////////////////////////////// - - /** - * Update the filtered events data structure. - * @param aInsertedToFront indicates whether events were added to front of - * the events. If true, then the current first event must still exist - * in the list after the filter is applied. - */ - private void updateFilteredEvents(boolean aInsertedToFront) { - final long start = System.currentTimeMillis(); - final List filtered = new ArrayList(); - final int size = mAllEvents.size(); - final Iterator it = mAllEvents.iterator(); - - while (it.hasNext()) { - final EventDetails event = (EventDetails) it.next(); - if (matchFilter(event)) { - filtered.add(event); - } - } - - final EventDetails lastFirst = (mFilteredEvents.length == 0) - ? null - : mFilteredEvents[0]; - mFilteredEvents = (EventDetails[]) filtered.toArray(EMPTY_LIST); - - if (aInsertedToFront && (lastFirst != null)) { - final int index = filtered.indexOf(lastFirst); - if (index < 1) { - LOG.warn("In strange state"); - fireTableDataChanged(); - } else { - fireTableRowsInserted(0, index - 1); - } - } else { - fireTableDataChanged(); - } - - final long end = System.currentTimeMillis(); - LOG.debug("Total time [ms]: " + (end - start) - + " in update, size: " + size); - } - - /** - * Returns whether an event matches the filters. - * - * @param aEvent the event to check for a match - * @return whether the event matches - */ - private boolean matchFilter(EventDetails aEvent) { - if (aEvent.getPriority().isGreaterOrEqual(mPriorityFilter) && - (aEvent.getThreadName().indexOf(mThreadFilter) >= 0) && - (aEvent.getCategoryName().indexOf(mCategoryFilter) >= 0) && - ((mNDCFilter.length() == 0) || - ((aEvent.getNDC() != null) && - (aEvent.getNDC().indexOf(mNDCFilter) >= 0)))) - { - final String rm = aEvent.getMessage(); - if (rm == null) { - // only match if we have not filtering in place - return (mMessageFilter.length() == 0); - } else { - return (rm.indexOf(mMessageFilter) >= 0); - } - } - - return false; // by default not match - } -} diff --git a/java/src/org/apache/log4j/chainsaw/XMLFileHandler.java b/java/src/org/apache/log4j/chainsaw/XMLFileHandler.java deleted file mode 100644 index 2f9af51..0000000 --- a/java/src/org/apache/log4j/chainsaw/XMLFileHandler.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.chainsaw; - -import java.util.StringTokenizer; -import org.apache.log4j.Level; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -/** - * A content handler for document containing Log4J events logged using the - * XMLLayout class. It will create events and add them to a supplied model. - * - * @author Oliver Burn - * @version 1.0 - */ -class XMLFileHandler - extends DefaultHandler -{ - /** represents the event tag **/ - private static final String TAG_EVENT = "log4j:event"; - /** represents the message tag **/ - private static final String TAG_MESSAGE = "log4j:message"; - /** represents the ndc tag **/ - private static final String TAG_NDC = "log4j:NDC"; - /** represents the throwable tag **/ - private static final String TAG_THROWABLE = "log4j:throwable"; - /** represents the location info tag **/ - private static final String TAG_LOCATION_INFO = "log4j:locationInfo"; - - /** where to put the events **/ - private final MyTableModel mModel; - /** the number of events in the document **/ - private int mNumEvents; - - /** the time of the event **/ - private long mTimeStamp; - /** the priority (level) of the event **/ - private Level mLevel; - /** the category of the event **/ - private String mCategoryName; - /** the NDC for the event **/ - private String mNDC; - /** the thread for the event **/ - private String mThreadName; - /** the msg for the event **/ - private String mMessage; - /** the throwable details the event **/ - private String[] mThrowableStrRep; - /** the location details for the event **/ - private String mLocationDetails; - /** buffer for collecting text **/ - private final StringBuffer mBuf = new StringBuffer(); - - /** - * Creates a new XMLFileHandler instance. - * - * @param aModel where to add the events - */ - XMLFileHandler(MyTableModel aModel) { - mModel = aModel; - } - - /** @see DefaultHandler **/ - public void startDocument() - throws SAXException - { - mNumEvents = 0; - } - - /** @see DefaultHandler **/ - public void characters(char[] aChars, int aStart, int aLength) { - mBuf.append(String.valueOf(aChars, aStart, aLength)); - } - - /** @see DefaultHandler **/ - public void endElement(String aNamespaceURI, - String aLocalName, - String aQName) - { - if (TAG_EVENT.equals(aQName)) { - addEvent(); - resetData(); - } else if (TAG_NDC.equals(aQName)) { - mNDC = mBuf.toString(); - } else if (TAG_MESSAGE.equals(aQName)) { - mMessage = mBuf.toString(); - } else if (TAG_THROWABLE.equals(aQName)) { - final StringTokenizer st = - new StringTokenizer(mBuf.toString(), "\n\t"); - mThrowableStrRep = new String[st.countTokens()]; - if (mThrowableStrRep.length > 0) { - mThrowableStrRep[0] = st.nextToken(); - for (int i = 1; i < mThrowableStrRep.length; i++) { - mThrowableStrRep[i] = "\t" + st.nextToken(); - } - } - } - } - - /** @see DefaultHandler **/ - public void startElement(String aNamespaceURI, - String aLocalName, - String aQName, - Attributes aAtts) - { - mBuf.setLength(0); - - if (TAG_EVENT.equals(aQName)) { - mThreadName = aAtts.getValue("thread"); - mTimeStamp = Long.parseLong(aAtts.getValue("timestamp")); - mCategoryName = aAtts.getValue("logger"); - mLevel = Level.toLevel(aAtts.getValue("level")); - } else if (TAG_LOCATION_INFO.equals(aQName)) { - mLocationDetails = aAtts.getValue("class") + "." - + aAtts.getValue("method") - + "(" + aAtts.getValue("file") + ":" + aAtts.getValue("line") - + ")"; - } - } - - /** @return the number of events in the document **/ - int getNumEvents() { - return mNumEvents; - } - - //////////////////////////////////////////////////////////////////////////// - // Private methods - //////////////////////////////////////////////////////////////////////////// - - /** Add an event to the model **/ - private void addEvent() { - mModel.addEvent(new EventDetails(mTimeStamp, - mLevel, - mCategoryName, - mNDC, - mThreadName, - mMessage, - mThrowableStrRep, - mLocationDetails)); - mNumEvents++; - } - - /** Reset the data for an event **/ - private void resetData() { - mTimeStamp = 0; - mLevel = null; - mCategoryName = null; - mNDC = null; - mThreadName = null; - mMessage = null; - mThrowableStrRep = null; - mLocationDetails = null; - } -} diff --git a/java/src/org/apache/log4j/chainsaw/doc-files/screen_01.png b/java/src/org/apache/log4j/chainsaw/doc-files/screen_01.png deleted file mode 100644 index 1a9c26dbc21aef199b25682a08bc0b3996269bce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23150 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 K#x%#oIs6}uwm7x` diff --git a/java/src/org/apache/log4j/chainsaw/package.html b/java/src/org/apache/log4j/chainsaw/package.html deleted file mode 100644 index 5b01480..0000000 --- a/java/src/org/apache/log4j/chainsaw/package.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - Chainsaw Tool - - - - -

Chainsaw is a GUI log viewer and filter for the log4j -package. By default it listens for LoggingEvent objects sent using -the SocketAppender and -displays them in a table. The events can be filtered based on:

- -
    -
  • Level
  • - -
  • Thread name
  • - -
  • Logger
  • -
  • Message
  • - -
  • NDC
  • -
- -

All the details for each event can be displayed by selecting - the event in the table.

- -

Chainsaw also supports loading a events logged to a file using - the XMLLayout format. This - is great for analysing log files, and means you do not need to - keep Chainsaw running continously. It is easy to add support - for loading events from other sources like JDBC.

- -

A picture is worth a thousand words:

- -

Screen shot of chainsaw.

- -

Finally, why is it called chainsaw? - Because it cuts your log (file) down to size. :-) -

- - -

Requirements

- -

Chainsaw is based on the Swing API which requires JDK 1.2 or later.

- - -

Running chainsaw

- -

Setup

-

You need to include the log4j.jar in the classpath. - -

Usage

- -

The command line usage is:

- -
  java -D<property>=<value> org.apache.log4j.chainsaw.Main 
- -

The default behaviour of chainsaw can be changed by setting system properties - using the -D<property>=<value> arguments to java. The - following table describes what properties can be set:

- - - - - - - - - - - -
PropertyDescription
chainsaw.portIndicates which port to listen for connections on. Defaults - to "4445". -
- -

Configuring Log4J

- -

You will need to configure log4j to send logging events to - Chainsaw. Here is a sample log4j.properties file - for sending logging events to Chainsaw.

- -
-log4j.rootLogger=DEBUG, CHAINSAW_CLIENT
-
-log4j.appender.CHAINSAW_CLIENT=org.apache.log4j.net.SocketAppender
-log4j.appender.CHAINSAW_CLIENT.RemoteHost=localhost
-log4j.appender.CHAINSAW_CLIENT.Port=4445
-log4j.appender.CHAINSAW_CLIENT.LocationInfo=true
-
- - - - - \ No newline at end of file diff --git a/java/src/org/apache/log4j/config/PropertyGetter.java b/java/src/org/apache/log4j/config/PropertyGetter.java deleted file mode 100644 index 5233699..0000000 --- a/java/src/org/apache/log4j/config/PropertyGetter.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.config; - -import org.apache.log4j.Priority; -import org.apache.log4j.helpers.LogLog; - -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.io.InterruptedIOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - - -/** - Used for inferring configuration information for a log4j's component. - - @author Anders Kristensen - */ -public class PropertyGetter { - protected static final Object[] NULL_ARG = new Object[] {}; - protected Object obj; - protected PropertyDescriptor[] props; - - public interface PropertyCallback { - void foundProperty(Object obj, String prefix, String name, Object value); - } - - /** - Create a new PropertyGetter for the specified Object. This is done - in prepartion for invoking {@link - #getProperties(PropertyGetter.PropertyCallback, String)} one or - more times. - - @param obj the object for which to set properties */ - public - PropertyGetter(Object obj) throws IntrospectionException { - BeanInfo bi = Introspector.getBeanInfo(obj.getClass()); - props = bi.getPropertyDescriptors(); - this.obj = obj; - } - - public - static - void getProperties(Object obj, PropertyCallback callback, String prefix) { - try { - new PropertyGetter(obj).getProperties(callback, prefix); - } catch (IntrospectionException ex) { - LogLog.error("Failed to introspect object " + obj, ex); - } - } - - public - void getProperties(PropertyCallback callback, String prefix) { - for (int i = 0; i < props.length; i++) { - Method getter = props[i].getReadMethod(); - if (getter == null) continue; - if (!isHandledType(getter.getReturnType())) { - //System.err.println("Ignoring " + props[i].getName() +" " + getter.getReturnType()); - continue; - } - String name = props[i].getName(); - try { - Object result = getter.invoke(obj, NULL_ARG); - //System.err.println("PROP " + name +": " + result); - if (result != null) { - callback.foundProperty(obj, prefix, name, result); - } - } catch (IllegalAccessException ex) { - LogLog.warn("Failed to get value of property " + name); - } catch (InvocationTargetException ex) { - if (ex.getTargetException() instanceof InterruptedException - || ex.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.warn("Failed to get value of property " + name); - } catch (RuntimeException ex) { - LogLog.warn("Failed to get value of property " + name); - } - } - } - - protected - boolean isHandledType(Class type) { - return String.class.isAssignableFrom(type) || - Integer.TYPE.isAssignableFrom(type) || - Long.TYPE.isAssignableFrom(type) || - Boolean.TYPE.isAssignableFrom(type) || - Priority.class.isAssignableFrom(type); - } -} diff --git a/java/src/org/apache/log4j/config/PropertyPrinter.java b/java/src/org/apache/log4j/config/PropertyPrinter.java deleted file mode 100644 index 517079f..0000000 --- a/java/src/org/apache/log4j/config/PropertyPrinter.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.config; - -import org.apache.log4j.Appender; -import org.apache.log4j.Category; -import org.apache.log4j.Level; -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; - -import java.io.PrintWriter; -import java.util.Enumeration; -import java.util.Hashtable; - -/** - Prints the configuration of the log4j default hierarchy - (which needs to be auto-initialized) as a propoperties file - on a {@link PrintWriter}. - - @author Anders Kristensen - */ -public class PropertyPrinter implements PropertyGetter.PropertyCallback { - protected int numAppenders = 0; - protected Hashtable appenderNames = new Hashtable(); - protected Hashtable layoutNames = new Hashtable(); - protected PrintWriter out; - protected boolean doCapitalize; - - public - PropertyPrinter(PrintWriter out) { - this(out, false); - } - - public - PropertyPrinter(PrintWriter out, boolean doCapitalize) { - this.out = out; - this.doCapitalize = doCapitalize; - - print(out); - out.flush(); - } - - protected - String genAppName() { - return "A" + numAppenders++; - } - - /** - * Returns true if the specified appender name is considered to have - * been generated, that is, if it is of the form A[0-9]+. - */ - protected - boolean isGenAppName(String name) { - if (name.length() < 2 || name.charAt(0) != 'A') return false; - - for (int i = 0; i < name.length(); i++) { - if (name.charAt(i) < '0' || name.charAt(i) > '9') return false; - } - return true; - } - - /** - * Prints the configuration of the default log4j hierarchy as a Java - * properties file on the specified Writer. - * - *

N.B. print() can be invoked only once! - */ - public - void print(PrintWriter out) { - printOptions(out, Logger.getRootLogger()); - - Enumeration cats = LogManager.getCurrentLoggers(); - while (cats.hasMoreElements()) { - printOptions(out, (Logger) cats.nextElement()); - } - } - - /** - * @since 1.2.15 - */ - protected - void printOptions(PrintWriter out, Category cat) { - Enumeration appenders = cat.getAllAppenders(); - Level prio = cat.getLevel(); - String appenderString = (prio == null ? "" : prio.toString()); - - while (appenders.hasMoreElements()) { - Appender app = (Appender) appenders.nextElement(); - String name; - - if ((name = (String) appenderNames.get(app)) == null) { - - // first assign name to the appender - if ((name = app.getName()) == null || isGenAppName(name)) { - name = genAppName(); - } - appenderNames.put(app, name); - - printOptions(out, app, "log4j.appender."+name); - if (app.getLayout() != null) { - printOptions(out, app.getLayout(), "log4j.appender."+name+".layout"); - } - } - appenderString += ", " + name; - } - String catKey = (cat == Logger.getRootLogger()) - ? "log4j.rootLogger" - : "log4j.logger." + cat.getName(); - if (appenderString != "") { - out.println(catKey + "=" + appenderString); - } - if (!cat.getAdditivity() && cat != Logger.getRootLogger()) { - out.println("log4j.additivity." + cat.getName() + "=false"); - } - } - - protected void printOptions(PrintWriter out, Logger cat) { - printOptions(out, (Category) cat); - } - - protected - void printOptions(PrintWriter out, Object obj, String fullname) { - out.println(fullname + "=" + obj.getClass().getName()); - PropertyGetter.getProperties(obj, this, fullname + "."); - } - - public void foundProperty(Object obj, String prefix, String name, Object value) { - // XXX: Properties encode value.toString() - if (obj instanceof Appender && "name".equals(name)) { - return; - } - if (doCapitalize) { - name = capitalize(name); - } - out.println(prefix + name + "=" + value.toString()); - } - - public static String capitalize(String name) { - if (Character.isLowerCase(name.charAt(0))) { - if (name.length() == 1 || Character.isLowerCase(name.charAt(1))) { - StringBuffer newname = new StringBuffer(name); - newname.setCharAt(0, Character.toUpperCase(name.charAt(0))); - return newname.toString(); - } - } - return name; - } - - // for testing - public static void main(String[] args) { - new PropertyPrinter(new PrintWriter(System.out)); - } -} diff --git a/java/src/org/apache/log4j/config/PropertySetter.java b/java/src/org/apache/log4j/config/PropertySetter.java deleted file mode 100644 index 61d36ef..0000000 --- a/java/src/org/apache/log4j/config/PropertySetter.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contributors: Georg Lundesgaard - -package org.apache.log4j.config; - -import org.apache.log4j.Appender; -import org.apache.log4j.Level; -import org.apache.log4j.Priority; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.helpers.OptionConverter; -import org.apache.log4j.spi.OptionHandler; -import org.apache.log4j.spi.ErrorHandler; - -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.io.InterruptedIOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Enumeration; -import java.util.Properties; - -/** - General purpose Object property setter. Clients repeatedly invokes - {@link #setProperty setProperty(name,value)} in order to invoke setters - on the Object specified in the constructor. This class relies on the - JavaBeans {@link Introspector} to analyze the given Object Class using - reflection. - -

Usage: -

-     PropertySetter ps = new PropertySetter(anObject);
-     ps.set("name", "Joe");
-     ps.set("age", "32");
-     ps.set("isMale", "true");
-   
- will cause the invocations anObject.setName("Joe"), anObject.setAge(32), - and setMale(true) if such methods exist with those signatures. - Otherwise an {@link IntrospectionException} are thrown. - - @author Anders Kristensen - @since 1.1 - */ -public class PropertySetter { - protected Object obj; - protected PropertyDescriptor[] props; - - /** - Create a new PropertySetter for the specified Object. This is done - in prepartion for invoking {@link #setProperty} one or more times. - - @param obj the object for which to set properties - */ - public - PropertySetter(Object obj) { - this.obj = obj; - } - - /** - Uses JavaBeans {@link Introspector} to computer setters of object to be - configured. - */ - protected - void introspect() { - try { - BeanInfo bi = Introspector.getBeanInfo(obj.getClass()); - props = bi.getPropertyDescriptors(); - } catch (IntrospectionException ex) { - LogLog.error("Failed to introspect "+obj+": " + ex.getMessage()); - props = new PropertyDescriptor[0]; - } - } - - - /** - Set the properties of an object passed as a parameter in one - go. The properties are parsed relative to a - prefix. - - @param obj The object to configure. - @param properties A java.util.Properties containing keys and values. - @param prefix Only keys having the specified prefix will be set. - */ - public - static - void setProperties(Object obj, Properties properties, String prefix) { - new PropertySetter(obj).setProperties(properties, prefix); - } - - - /** - Set the properites for the object that match the - prefix passed as parameter. - - - */ - public - void setProperties(Properties properties, String prefix) { - int len = prefix.length(); - - for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); ) { - String key = (String) e.nextElement(); - - // handle only properties that start with the desired frefix. - if (key.startsWith(prefix)) { - - - // ignore key if it contains dots after the prefix - if (key.indexOf('.', len + 1) > 0) { - //System.err.println("----------Ignoring---["+key - // +"], prefix=["+prefix+"]."); - continue; - } - - String value = OptionConverter.findAndSubst(key, properties); - key = key.substring(len); - if (("layout".equals(key) || "errorhandler".equals(key)) && obj instanceof Appender) { - continue; - } - // - // if the property type is an OptionHandler - // (for example, triggeringPolicy of org.apache.log4j.rolling.RollingFileAppender) - PropertyDescriptor prop = getPropertyDescriptor(Introspector.decapitalize(key)); - if (prop != null - && OptionHandler.class.isAssignableFrom(prop.getPropertyType()) - && prop.getWriteMethod() != null) { - OptionHandler opt = (OptionHandler) - OptionConverter.instantiateByKey(properties, prefix + key, - prop.getPropertyType(), - null); - PropertySetter setter = new PropertySetter(opt); - setter.setProperties(properties, prefix + key + "."); - try { - prop.getWriteMethod().invoke(this.obj, new Object[] { opt }); - } catch(IllegalAccessException ex) { - LogLog.warn("Failed to set property [" + key + - "] to value \"" + value + "\". ", ex); - } catch(InvocationTargetException ex) { - if (ex.getTargetException() instanceof InterruptedException - || ex.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.warn("Failed to set property [" + key + - "] to value \"" + value + "\". ", ex); - } catch(RuntimeException ex) { - LogLog.warn("Failed to set property [" + key + - "] to value \"" + value + "\". ", ex); - } - continue; - } - - setProperty(key, value); - } - } - activate(); - } - - /** - Set a property on this PropertySetter's Object. If successful, this - method will invoke a setter method on the underlying Object. The - setter is the one for the specified property name and the value is - determined partly from the setter argument type and partly from the - value specified in the call to this method. - -

If the setter expects a String no conversion is necessary. - If it expects an int, then an attempt is made to convert 'value' - to an int using new Integer(value). If the setter expects a boolean, - the conversion is by new Boolean(value). - - @param name name of the property - @param value String value of the property - */ - public - void setProperty(String name, String value) { - if (value == null) return; - - name = Introspector.decapitalize(name); - PropertyDescriptor prop = getPropertyDescriptor(name); - - //LogLog.debug("---------Key: "+name+", type="+prop.getPropertyType()); - - if (prop == null) { - LogLog.warn("No such property [" + name + "] in "+ - obj.getClass().getName()+"." ); - } else { - try { - setProperty(prop, name, value); - } catch (PropertySetterException ex) { - LogLog.warn("Failed to set property [" + name + - "] to value \"" + value + "\". ", ex.rootCause); - } - } - } - - /** - Set the named property given a {@link PropertyDescriptor}. - - @param prop A PropertyDescriptor describing the characteristics - of the property to set. - @param name The named of the property to set. - @param value The value of the property. - */ - public - void setProperty(PropertyDescriptor prop, String name, String value) - throws PropertySetterException { - Method setter = prop.getWriteMethod(); - if (setter == null) { - throw new PropertySetterException("No setter for property ["+name+"]."); - } - Class[] paramTypes = setter.getParameterTypes(); - if (paramTypes.length != 1) { - throw new PropertySetterException("#params for setter != 1"); - } - - Object arg; - try { - arg = convertArg(value, paramTypes[0]); - } catch (Throwable t) { - throw new PropertySetterException("Conversion to type ["+paramTypes[0]+ - "] failed. Reason: "+t); - } - if (arg == null) { - throw new PropertySetterException( - "Conversion to type ["+paramTypes[0]+"] failed."); - } - LogLog.debug("Setting property [" + name + "] to [" +arg+"]."); - try { - setter.invoke(obj, new Object[] { arg }); - } catch (IllegalAccessException ex) { - throw new PropertySetterException(ex); - } catch (InvocationTargetException ex) { - if (ex.getTargetException() instanceof InterruptedException - || ex.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - throw new PropertySetterException(ex); - } catch (RuntimeException ex) { - throw new PropertySetterException(ex); - } - } - - - /** - Convert val a String parameter to an object of a - given type. - */ - protected - Object convertArg(String val, Class type) { - if(val == null) - return null; - - String v = val.trim(); - if (String.class.isAssignableFrom(type)) { - return val; - } else if (Integer.TYPE.isAssignableFrom(type)) { - return new Integer(v); - } else if (Long.TYPE.isAssignableFrom(type)) { - return new Long(v); - } else if (Boolean.TYPE.isAssignableFrom(type)) { - if ("true".equalsIgnoreCase(v)) { - return Boolean.TRUE; - } else if ("false".equalsIgnoreCase(v)) { - return Boolean.FALSE; - } - } else if (Priority.class.isAssignableFrom(type)) { - return OptionConverter.toLevel(v, (Level) Level.DEBUG); - } else if (ErrorHandler.class.isAssignableFrom(type)) { - return OptionConverter.instantiateByClassName(v, - ErrorHandler.class, null); - } - return null; - } - - - protected - PropertyDescriptor getPropertyDescriptor(String name) { - if (props == null) introspect(); - - for (int i = 0; i < props.length; i++) { - if (name.equals(props[i].getName())) { - return props[i]; - } - } - return null; - } - - public - void activate() { - if (obj instanceof OptionHandler) { - ((OptionHandler) obj).activateOptions(); - } - } -} diff --git a/java/src/org/apache/log4j/config/PropertySetterException.java b/java/src/org/apache/log4j/config/PropertySetterException.java deleted file mode 100644 index c6314cc..0000000 --- a/java/src/org/apache/log4j/config/PropertySetterException.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.config; - -/** - * Thrown when an error is encountered whilst attempting to set a property - * using the {@link PropertySetter} utility class. - * - * @author Anders Kristensen - * @since 1.1 - */ -public class PropertySetterException extends Exception { - private static final long serialVersionUID = -1352613734254235861L; - protected Throwable rootCause; - - public - PropertySetterException(String msg) { - super(msg); - } - - public - PropertySetterException(Throwable rootCause) - { - super(); - this.rootCause = rootCause; - } - - /** - Returns descriptive text on the cause of this exception. - */ - public - String getMessage() { - String msg = super.getMessage(); - if (msg == null && rootCause != null) { - msg = rootCause.getMessage(); - } - return msg; - } -} diff --git a/java/src/org/apache/log4j/config/package.html b/java/src/org/apache/log4j/config/package.html deleted file mode 100644 index 973dce4..0000000 --- a/java/src/org/apache/log4j/config/package.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -Package used in getting/setting component properties. - - \ No newline at end of file diff --git a/java/src/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java b/java/src/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java deleted file mode 100644 index 737ee85..0000000 --- a/java/src/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import java.util.Date; -import java.util.Calendar; -import java.util.TimeZone; -import java.text.FieldPosition; -import java.text.ParsePosition; -import java.text.DateFormat; - - -/** - Formats a {@link Date} in the format "HH:mm:ss,SSS" for example, - "15:49:37,459". - - @author Ceki Gülcü - @author Andrew Vajoczki - - @since 0.7.5 -*/ -public class AbsoluteTimeDateFormat extends DateFormat { - private static final long serialVersionUID = -388856345976723342L; - - /** - String constant used to specify {@link - org.apache.log4j.helpers.AbsoluteTimeDateFormat} in layouts. Current - value is ABSOLUTE. */ - public final static String ABS_TIME_DATE_FORMAT = "ABSOLUTE"; - - /** - String constant used to specify {@link - org.apache.log4j.helpers.DateTimeDateFormat} in layouts. Current - value is DATE. - */ - public final static String DATE_AND_TIME_DATE_FORMAT = "DATE"; - - /** - String constant used to specify {@link - org.apache.log4j.helpers.ISO8601DateFormat} in layouts. Current - value is ISO8601. - */ - public final static String ISO8601_DATE_FORMAT = "ISO8601"; - - public - AbsoluteTimeDateFormat() { - setCalendar(Calendar.getInstance()); - } - - public - AbsoluteTimeDateFormat(TimeZone timeZone) { - setCalendar(Calendar.getInstance(timeZone)); - } - - private static long previousTime; - private static char[] previousTimeWithoutMillis = new char[9]; // "HH:mm:ss." - - /** - Appends to sbuf the time in the format - "HH:mm:ss,SSS" for example, "15:49:37,459" - - @param date the date to format - @param sbuf the string buffer to write to - @param fieldPosition remains untouched - */ - public - StringBuffer format(Date date, StringBuffer sbuf, - FieldPosition fieldPosition) { - - long now = date.getTime(); - int millis = (int)(now % 1000); - - if ((now - millis) != previousTime || previousTimeWithoutMillis[0] == 0) { - // We reach this point at most once per second - // across all threads instead of each time format() - // is called. This saves considerable CPU time. - - calendar.setTime(date); - - int start = sbuf.length(); - - int hour = calendar.get(Calendar.HOUR_OF_DAY); - if(hour < 10) { - sbuf.append('0'); - } - sbuf.append(hour); - sbuf.append(':'); - - int mins = calendar.get(Calendar.MINUTE); - if(mins < 10) { - sbuf.append('0'); - } - sbuf.append(mins); - sbuf.append(':'); - - int secs = calendar.get(Calendar.SECOND); - if(secs < 10) { - sbuf.append('0'); - } - sbuf.append(secs); - sbuf.append(','); - - // store the time string for next time to avoid recomputation - sbuf.getChars(start, sbuf.length(), previousTimeWithoutMillis, 0); - - previousTime = now - millis; - } - else { - sbuf.append(previousTimeWithoutMillis); - } - - - - if(millis < 100) - sbuf.append('0'); - if(millis < 10) - sbuf.append('0'); - - sbuf.append(millis); - return sbuf; - } - - /** - This method does not do anything but return null. - */ - public - Date parse(String s, ParsePosition pos) { - return null; - } -} diff --git a/java/src/org/apache/log4j/helpers/AppenderAttachableImpl.java b/java/src/org/apache/log4j/helpers/AppenderAttachableImpl.java deleted file mode 100644 index 0e8cd38..0000000 --- a/java/src/org/apache/log4j/helpers/AppenderAttachableImpl.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import org.apache.log4j.spi.AppenderAttachable; -import org.apache.log4j.spi.LoggingEvent; - -import org.apache.log4j.Appender; -import java.util.Vector; -import java.util.Enumeration; - -/** - A straightforward implementation of the {@link AppenderAttachable} - interface. - - @author Ceki Gülcü - @since version 0.9.1 */ -public class AppenderAttachableImpl implements AppenderAttachable { - - /** Array of appenders. */ - protected Vector appenderList; - - /** - Attach an appender. If the appender is already in the list in - won't be added again. - */ - public - void addAppender(Appender newAppender) { - // Null values for newAppender parameter are strictly forbidden. - if(newAppender == null) - return; - - if(appenderList == null) { - appenderList = new Vector(1); - } - if(!appenderList.contains(newAppender)) - appenderList.addElement(newAppender); - } - - /** - Call the doAppend method on all attached appenders. */ - public - int appendLoopOnAppenders(LoggingEvent event) { - int size = 0; - Appender appender; - - if(appenderList != null) { - size = appenderList.size(); - for(int i = 0; i < size; i++) { - appender = (Appender) appenderList.elementAt(i); - appender.doAppend(event); - } - } - return size; - } - - - /** - Get all attached appenders as an Enumeration. If there are no - attached appenders null is returned. - - @return Enumeration An enumeration of attached appenders. - */ - public - Enumeration getAllAppenders() { - if(appenderList == null) - return null; - else - return appenderList.elements(); - } - - /** - Look for an attached appender named as name. - -

Return the appender with that name if in the list. Return null - otherwise. - - */ - public - Appender getAppender(String name) { - if(appenderList == null || name == null) - return null; - - int size = appenderList.size(); - Appender appender; - for(int i = 0; i < size; i++) { - appender = (Appender) appenderList.elementAt(i); - if(name.equals(appender.getName())) - return appender; - } - return null; - } - - - /** - Returns true if the specified appender is in the - list of attached appenders, false otherwise. - - @since 1.2 */ - public - boolean isAttached(Appender appender) { - if(appenderList == null || appender == null) - return false; - - int size = appenderList.size(); - Appender a; - for(int i = 0; i < size; i++) { - a = (Appender) appenderList.elementAt(i); - if(a == appender) - return true; - } - return false; - } - - - - /** - * Remove and close all previously attached appenders. - * */ - public - void removeAllAppenders() { - if(appenderList != null) { - int len = appenderList.size(); - for(int i = 0; i < len; i++) { - Appender a = (Appender) appenderList.elementAt(i); - a.close(); - } - appenderList.removeAllElements(); - appenderList = null; - } - } - - - /** - Remove the appender passed as parameter form the list of attached - appenders. */ - public - void removeAppender(Appender appender) { - if(appender == null || appenderList == null) - return; - appenderList.removeElement(appender); - } - - - /** - Remove the appender with the name passed as parameter form the - list of appenders. - */ - public - void removeAppender(String name) { - if(name == null || appenderList == null) return; - int size = appenderList.size(); - for(int i = 0; i < size; i++) { - if(name.equals(((Appender)appenderList.elementAt(i)).getName())) { - appenderList.removeElementAt(i); - break; - } - } - } - -} diff --git a/java/src/org/apache/log4j/helpers/BoundedFIFO.java b/java/src/org/apache/log4j/helpers/BoundedFIFO.java deleted file mode 100644 index e5ce96c..0000000 --- a/java/src/org/apache/log4j/helpers/BoundedFIFO.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contributors: Mathias Bogaert -// joelr@viair.com - -package org.apache.log4j.helpers; - -import org.apache.log4j.spi.LoggingEvent; - -/** - BoundedFIFO serves as the bounded first-in-first-out - buffer heavily used by the {@link org.apache.log4j.AsyncAppender}. - - @author Ceki Gülcü - @since version 0.9.1 */ -public class BoundedFIFO { - - LoggingEvent[] buf; - int numElements = 0; - int first = 0; - int next = 0; - int maxSize; - - /** - Instantiate a new BoundedFIFO with a maximum size passed as argument. - */ - public - BoundedFIFO(int maxSize) { - if(maxSize < 1) { - throw new IllegalArgumentException("The maxSize argument ("+maxSize+ - ") is not a positive integer."); - } - this.maxSize = maxSize; - buf = new LoggingEvent[maxSize]; - } - - /** - Get the first element in the buffer. Returns null if - there are no elements in the buffer. */ - public - LoggingEvent get() { - if(numElements == 0) - return null; - - LoggingEvent r = buf[first]; - buf[first] = null; // help garbage collection - - if(++first == maxSize) { - first = 0; - } - numElements--; - return r; - } - - /** - Place a {@link LoggingEvent} in the buffer. If the buffer is full - then the event is silently dropped. It is the caller's - responsability to make sure that the buffer has free space. */ - public - void put(LoggingEvent o) { - if(numElements != maxSize) { - buf[next] = o; - if(++next == maxSize) { - next = 0; - } - numElements++; - } - } - - /** - Get the maximum size of the buffer. - */ - public - int getMaxSize() { - return maxSize; - } - - /** - Return true if the buffer is full, that is, whether - the number of elements in the buffer equals the buffer size. */ - public - boolean isFull() { - return numElements == maxSize; - } - - /** - Get the number of elements in the buffer. This number is - guaranteed to be in the range 0 to maxSize - (inclusive). - */ - public - int length() { - return numElements; - } - - - int min(int a, int b) { - return a < b ? a : b; - } - - - /** - Resize the buffer to a new size. If the new size is smaller than - the old size events might be lost. - - @since 1.1 - */ - synchronized - public - void resize(int newSize) { - if(newSize == maxSize) - return; - - - LoggingEvent[] tmp = new LoggingEvent[newSize]; - - // we should not copy beyond the buf array - int len1 = maxSize - first; - - // we should not copy beyond the tmp array - len1 = min(len1, newSize); - - // er.. how much do we actually need to copy? - // We should not copy more than the actual number of elements. - len1 = min(len1, numElements); - - // Copy from buf starting a first, to tmp, starting at position 0, len1 elements. - System.arraycopy(buf, first, tmp, 0, len1); - - // Are there any uncopied elements and is there still space in the new array? - int len2 = 0; - if((len1 < numElements) && (len1 < newSize)) { - len2 = numElements - len1; - len2 = min(len2, newSize - len1); - System.arraycopy(buf, 0, tmp, len1, len2); - } - - this.buf = tmp; - this.maxSize = newSize; - this.first=0; - this.numElements = len1+len2; - this.next = this.numElements; - if(this.next == this.maxSize) // this should never happen, but again, it just might. - this.next = 0; - } - - - /** - Returns true if there is just one element in the - buffer. In other words, if there were no elements before the last - {@link #put} operation completed. */ - public - boolean wasEmpty() { - return numElements == 1; - } - - /** - Returns true if the number of elements in the - buffer plus 1 equals the maximum buffer size, returns - false otherwise. */ - public - boolean wasFull() { - return (numElements+1 == maxSize); - } - -} diff --git a/java/src/org/apache/log4j/helpers/CountingQuietWriter.java b/java/src/org/apache/log4j/helpers/CountingQuietWriter.java deleted file mode 100644 index 55199e4..0000000 --- a/java/src/org/apache/log4j/helpers/CountingQuietWriter.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import java.io.Writer; -import java.io.IOException; - -import org.apache.log4j.spi.ErrorHandler; -import org.apache.log4j.spi.ErrorCode; - -/** - Counts the number of bytes written. - - @author Heinz Richter, heinz.richter@frogdot.com - @since 0.8.1 - - */ -public class CountingQuietWriter extends QuietWriter { - - protected long count; - - public - CountingQuietWriter(Writer writer, ErrorHandler eh) { - super(writer, eh); - } - - public - void write(String string) { - try { - out.write(string); - count += string.length(); - } - catch(IOException e) { - errorHandler.error("Write failure.", e, ErrorCode.WRITE_FAILURE); - } - } - - public - long getCount() { - return count; - } - - public - void setCount(long count) { - this.count = count; - } - -} diff --git a/java/src/org/apache/log4j/helpers/CyclicBuffer.java b/java/src/org/apache/log4j/helpers/CyclicBuffer.java deleted file mode 100644 index 9077847..0000000 --- a/java/src/org/apache/log4j/helpers/CyclicBuffer.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import org.apache.log4j.spi.LoggingEvent; - -/** - - CyclicBuffer is used by other appenders to hold {@link LoggingEvent - LoggingEvents} for immediate or differed display. - -

This buffer gives read access to any element in the buffer not - just the first or last element. - - @author Ceki Gülcü - @since 0.9.0 - - */ -public class CyclicBuffer { - - LoggingEvent[] ea; - int first; - int last; - int numElems; - int maxSize; - - /** - Instantiate a new CyclicBuffer of at most maxSize events. - - The maxSize argument must a positive integer. - - @param maxSize The maximum number of elements in the buffer. - */ - public CyclicBuffer(int maxSize) throws IllegalArgumentException { - if(maxSize < 1) { - throw new IllegalArgumentException("The maxSize argument ("+maxSize+ - ") is not a positive integer."); - } - this.maxSize = maxSize; - ea = new LoggingEvent[maxSize]; - first = 0; - last = 0; - numElems = 0; - } - - /** - Add an event as the last event in the buffer. - - */ - public - void add(LoggingEvent event) { - ea[last] = event; - if(++last == maxSize) - last = 0; - - if(numElems < maxSize) - numElems++; - else if(++first == maxSize) - first = 0; - } - - - /** - Get the ith oldest event currently in the buffer. If - i is outside the range 0 to the number of elements - currently in the buffer, then null is returned. - - - */ - public - LoggingEvent get(int i) { - if(i < 0 || i >= numElems) - return null; - - return ea[(first + i) % maxSize]; - } - - public - int getMaxSize() { - return maxSize; - } - - /** - Get the oldest (first) element in the buffer. The oldest element - is removed from the buffer. - */ - public - LoggingEvent get() { - LoggingEvent r = null; - if(numElems > 0) { - numElems--; - r = ea[first]; - ea[first] = null; - if(++first == maxSize) - first = 0; - } - return r; - } - - /** - Get the number of elements in the buffer. This number is - guaranteed to be in the range 0 to maxSize - (inclusive). - */ - public - int length() { - return numElems; - } - - /** - Resize the cyclic buffer to newSize. - - @throws IllegalArgumentException if newSize is negative. - */ - public - void resize(int newSize) { - if(newSize < 0) { - throw new IllegalArgumentException("Negative array size ["+newSize+ - "] not allowed."); - } - if(newSize == numElems) - return; // nothing to do - - LoggingEvent[] temp = new LoggingEvent[newSize]; - - int loopLen = newSize < numElems ? newSize : numElems; - - for(int i = 0; i < loopLen; i++) { - temp[i] = ea[first]; - ea[first] = null; - if(++first == numElems) - first = 0; - } - ea = temp; - first = 0; - numElems = loopLen; - maxSize = newSize; - if (loopLen == newSize) { - last = 0; - } else { - last = loopLen; - } - } -} diff --git a/java/src/org/apache/log4j/helpers/DateLayout.java b/java/src/org/apache/log4j/helpers/DateLayout.java deleted file mode 100644 index 383ef38..0000000 --- a/java/src/org/apache/log4j/helpers/DateLayout.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import org.apache.log4j.Layout; -import org.apache.log4j.spi.LoggingEvent; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; -import java.text.FieldPosition; - - -/** - This abstract layout takes care of all the date related options and - formatting work. - - - @author Ceki Gülcü - */ -abstract public class DateLayout extends Layout { - - /** - String constant designating no time information. Current value of - this constant is NULL. - - */ - public final static String NULL_DATE_FORMAT = "NULL"; - - /** - String constant designating relative time. Current value of - this constant is RELATIVE. - */ - public final static String RELATIVE_TIME_DATE_FORMAT = "RELATIVE"; - - protected FieldPosition pos = new FieldPosition(0); - - /** - @deprecated Options are now handled using the JavaBeans paradigm. - This constant is not longer needed and will be removed in the - near term. - */ - final static public String DATE_FORMAT_OPTION = "DateFormat"; - - /** - @deprecated Options are now handled using the JavaBeans paradigm. - This constant is not longer needed and will be removed in the - near term. - */ - final static public String TIMEZONE_OPTION = "TimeZone"; - - private String timeZoneID; - private String dateFormatOption; - - protected DateFormat dateFormat; - protected Date date = new Date(); - - /** - @deprecated Use the setter method for the option directly instead - of the generic setOption method. - */ - public - String[] getOptionStrings() { - return new String[] {DATE_FORMAT_OPTION, TIMEZONE_OPTION}; - } - - /** - @deprecated Use the setter method for the option directly instead - of the generic setOption method. - */ - public - void setOption(String option, String value) { - if(option.equalsIgnoreCase(DATE_FORMAT_OPTION)) { - dateFormatOption = value.toUpperCase(); - } else if(option.equalsIgnoreCase(TIMEZONE_OPTION)) { - timeZoneID = value; - } - } - - - /** - The value of the DateFormat option should be either an - argument to the constructor of {@link SimpleDateFormat} or one of - the srings "NULL", "RELATIVE", "ABSOLUTE", "DATE" or "ISO8601. - */ - public - void setDateFormat(String dateFormat) { - if (dateFormat != null) { - dateFormatOption = dateFormat; - } - setDateFormat(dateFormatOption, TimeZone.getDefault()); - } - - /** - Returns value of the DateFormat option. - */ - public - String getDateFormat() { - return dateFormatOption; - } - - /** - The TimeZoneID option is a time zone ID string in the format - expected by the {@link TimeZone#getTimeZone} method. - */ - public - void setTimeZone(String timeZone) { - this.timeZoneID = timeZone; - } - - /** - Returns value of the TimeZone option. - */ - public - String getTimeZone() { - return timeZoneID; - } - - public - void activateOptions() { - setDateFormat(dateFormatOption); - if(timeZoneID != null && dateFormat != null) { - dateFormat.setTimeZone(TimeZone.getTimeZone(timeZoneID)); - } - } - - public - void dateFormat(StringBuffer buf, LoggingEvent event) { - if(dateFormat != null) { - date.setTime(event.timeStamp); - dateFormat.format(date, buf, this.pos); - buf.append(' '); - } - } - - /** - Sets the {@link DateFormat} used to format time and date in the - zone determined by timeZone. - */ - public - void setDateFormat(DateFormat dateFormat, TimeZone timeZone) { - this.dateFormat = dateFormat; - this.dateFormat.setTimeZone(timeZone); - } - - /** - Sets the DateFormat used to format date and time in the time zone - determined by timeZone parameter. The {@link DateFormat} used - will depend on the dateFormatType. - -

The recognized types are {@link #NULL_DATE_FORMAT}, {@link - #RELATIVE_TIME_DATE_FORMAT} {@link - AbsoluteTimeDateFormat#ABS_TIME_DATE_FORMAT}, {@link - AbsoluteTimeDateFormat#DATE_AND_TIME_DATE_FORMAT} and {@link - AbsoluteTimeDateFormat#ISO8601_DATE_FORMAT}. If the - dateFormatType is not one of the above, then the - argument is assumed to be a date pattern for {@link - SimpleDateFormat}. - */ - public - void setDateFormat(String dateFormatType, TimeZone timeZone) { - if(dateFormatType == null) { - this.dateFormat = null; - return; - } - - if(dateFormatType.equalsIgnoreCase(NULL_DATE_FORMAT)) { - this.dateFormat = null; - } else if (dateFormatType.equalsIgnoreCase(RELATIVE_TIME_DATE_FORMAT)) { - this.dateFormat = new RelativeTimeDateFormat(); - } else if(dateFormatType.equalsIgnoreCase( - AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT)) { - this.dateFormat = new AbsoluteTimeDateFormat(timeZone); - } else if(dateFormatType.equalsIgnoreCase( - AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT)) { - this.dateFormat = new DateTimeDateFormat(timeZone); - } else if(dateFormatType.equalsIgnoreCase( - AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT)) { - this.dateFormat = new ISO8601DateFormat(timeZone); - } else { - this.dateFormat = new SimpleDateFormat(dateFormatType); - this.dateFormat.setTimeZone(timeZone); - } - } -} diff --git a/java/src/org/apache/log4j/helpers/DateTimeDateFormat.java b/java/src/org/apache/log4j/helpers/DateTimeDateFormat.java deleted file mode 100644 index 559f731..0000000 --- a/java/src/org/apache/log4j/helpers/DateTimeDateFormat.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import java.util.Calendar; -import java.util.TimeZone; -import java.util.Date; -import java.text.FieldPosition; -import java.text.ParsePosition; -import java.text.DateFormatSymbols; - -/** - Formats a {@link Date} in the format "dd MMM yyyy HH:mm:ss,SSS" for example, - "06 Nov 1994 15:49:37,459". - - @author Ceki Gülcü - @since 0.7.5 -*/ -public class DateTimeDateFormat extends AbsoluteTimeDateFormat { - private static final long serialVersionUID = 5547637772208514971L; - - String[] shortMonths; - - public - DateTimeDateFormat() { - super(); - shortMonths = new DateFormatSymbols().getShortMonths(); - } - - public - DateTimeDateFormat(TimeZone timeZone) { - this(); - setCalendar(Calendar.getInstance(timeZone)); - } - - /** - Appends to sbuf the date in the format "dd MMM yyyy - HH:mm:ss,SSS" for example, "06 Nov 1994 08:49:37,459". - - @param sbuf the string buffer to write to - */ - public - StringBuffer format(Date date, StringBuffer sbuf, - FieldPosition fieldPosition) { - - calendar.setTime(date); - - int day = calendar.get(Calendar.DAY_OF_MONTH); - if(day < 10) - sbuf.append('0'); - sbuf.append(day); - sbuf.append(' '); - sbuf.append(shortMonths[calendar.get(Calendar.MONTH)]); - sbuf.append(' '); - - int year = calendar.get(Calendar.YEAR); - sbuf.append(year); - sbuf.append(' '); - - return super.format(date, sbuf, fieldPosition); - } - - /** - This method does not do anything but return null. - */ - public - Date parse(java.lang.String s, ParsePosition pos) { - return null; - } -} diff --git a/java/src/org/apache/log4j/helpers/FileWatchdog.java b/java/src/org/apache/log4j/helpers/FileWatchdog.java deleted file mode 100644 index b78a4af..0000000 --- a/java/src/org/apache/log4j/helpers/FileWatchdog.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contributors: Mathias Bogaert - -package org.apache.log4j.helpers; - -import java.io.File; - -/** - Check every now and then that a certain file has not changed. If it - has, then call the {@link #doOnChange} method. - - - @author Ceki Gülcü - @since version 0.9.1 */ -public abstract class FileWatchdog extends Thread { - - /** - The default delay between every file modification check, set to 60 - seconds. */ - static final public long DEFAULT_DELAY = 60000; - /** - The name of the file to observe for changes. - */ - protected String filename; - - /** - The delay to observe between every check. By default set {@link - #DEFAULT_DELAY}. */ - protected long delay = DEFAULT_DELAY; - - File file; - long lastModif = 0; - boolean warnedAlready = false; - boolean interrupted = false; - - protected - FileWatchdog(String filename) { - super("FileWatchdog"); - this.filename = filename; - file = new File(filename); - setDaemon(true); - checkAndConfigure(); - } - - /** - Set the delay to observe between each check of the file changes. - */ - public - void setDelay(long delay) { - this.delay = delay; - } - - abstract - protected - void doOnChange(); - - protected - void checkAndConfigure() { - boolean fileExists; - try { - fileExists = file.exists(); - } catch(SecurityException e) { - LogLog.warn("Was not allowed to read check file existance, file:["+ - filename+"]."); - interrupted = true; // there is no point in continuing - return; - } - - if(fileExists) { - long l = file.lastModified(); // this can also throw a SecurityException - if(l > lastModif) { // however, if we reached this point this - lastModif = l; // is very unlikely. - doOnChange(); - warnedAlready = false; - } - } else { - if(!warnedAlready) { - LogLog.debug("["+filename+"] does not exist."); - warnedAlready = true; - } - } - } - - public - void run() { - while(!interrupted) { - try { - Thread.sleep(delay); - } catch(InterruptedException e) { - // no interruption expected - } - checkAndConfigure(); - } - } -} diff --git a/java/src/org/apache/log4j/helpers/FormattingInfo.java b/java/src/org/apache/log4j/helpers/FormattingInfo.java deleted file mode 100644 index e158243..0000000 --- a/java/src/org/apache/log4j/helpers/FormattingInfo.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - - -/** - FormattingInfo instances contain the information obtained when parsing - formatting modifiers in conversion modifiers. - - @author Jim Cakalic - @author Ceki Gülcü - - @since 0.8.2 - */ -public class FormattingInfo { - int min = -1; - int max = 0x7FFFFFFF; - boolean leftAlign = false; - - void reset() { - min = -1; - max = 0x7FFFFFFF; - leftAlign = false; - } - - void dump() { - LogLog.debug("min="+min+", max="+max+", leftAlign="+leftAlign); - } -} - diff --git a/java/src/org/apache/log4j/helpers/ISO8601DateFormat.java b/java/src/org/apache/log4j/helpers/ISO8601DateFormat.java deleted file mode 100644 index 47b17d9..0000000 --- a/java/src/org/apache/log4j/helpers/ISO8601DateFormat.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import java.util.Calendar; -import java.util.TimeZone; -import java.util.Date; -import java.text.FieldPosition; -import java.text.ParsePosition; - -// Contributors: Arndt Schoenewald - -/** - Formats a {@link Date} in the format "yyyy-MM-dd HH:mm:ss,SSS" for example - "1999-11-27 15:49:37,459". - -

Refer to the summary of the - International Standard Date and Time Notation for more - information on this format. - - @author Ceki Gülcü - @author Andrew Vajoczki - - @since 0.7.5 -*/ -public class ISO8601DateFormat extends AbsoluteTimeDateFormat { - private static final long serialVersionUID = -759840745298755296L; - - public - ISO8601DateFormat() { - } - - public - ISO8601DateFormat(TimeZone timeZone) { - super(timeZone); - } - - static private long lastTime; - static private char[] lastTimeString = new char[20]; - - /** - Appends a date in the format "YYYY-mm-dd HH:mm:ss,SSS" - to sbuf. For example: "1999-11-27 15:49:37,459". - - @param sbuf the StringBuffer to write to - */ - public - StringBuffer format(Date date, StringBuffer sbuf, - FieldPosition fieldPosition) { - - long now = date.getTime(); - int millis = (int)(now % 1000); - - if ((now - millis) != lastTime || lastTimeString[0] == 0) { - // We reach this point at most once per second - // across all threads instead of each time format() - // is called. This saves considerable CPU time. - - calendar.setTime(date); - - int start = sbuf.length(); - - int year = calendar.get(Calendar.YEAR); - sbuf.append(year); - - String month; - switch(calendar.get(Calendar.MONTH)) { - case Calendar.JANUARY: month = "-01-"; break; - case Calendar.FEBRUARY: month = "-02-"; break; - case Calendar.MARCH: month = "-03-"; break; - case Calendar.APRIL: month = "-04-"; break; - case Calendar.MAY: month = "-05-"; break; - case Calendar.JUNE: month = "-06-"; break; - case Calendar.JULY: month = "-07-"; break; - case Calendar.AUGUST: month = "-08-"; break; - case Calendar.SEPTEMBER: month = "-09-"; break; - case Calendar.OCTOBER: month = "-10-"; break; - case Calendar.NOVEMBER: month = "-11-"; break; - case Calendar.DECEMBER: month = "-12-"; break; - default: month = "-NA-"; break; - } - sbuf.append(month); - - int day = calendar.get(Calendar.DAY_OF_MONTH); - if(day < 10) - sbuf.append('0'); - sbuf.append(day); - - sbuf.append(' '); - - int hour = calendar.get(Calendar.HOUR_OF_DAY); - if(hour < 10) { - sbuf.append('0'); - } - sbuf.append(hour); - sbuf.append(':'); - - int mins = calendar.get(Calendar.MINUTE); - if(mins < 10) { - sbuf.append('0'); - } - sbuf.append(mins); - sbuf.append(':'); - - int secs = calendar.get(Calendar.SECOND); - if(secs < 10) { - sbuf.append('0'); - } - sbuf.append(secs); - - sbuf.append(','); - - // store the time string for next time to avoid recomputation - sbuf.getChars(start, sbuf.length(), lastTimeString, 0); - lastTime = now - millis; - } - else { - sbuf.append(lastTimeString); - } - - - if (millis < 100) - sbuf.append('0'); - if (millis < 10) - sbuf.append('0'); - - sbuf.append(millis); - return sbuf; - } - - /** - This method does not do anything but return null. - */ - public - Date parse(java.lang.String s, ParsePosition pos) { - return null; - } -} - diff --git a/java/src/org/apache/log4j/helpers/Loader.java b/java/src/org/apache/log4j/helpers/Loader.java deleted file mode 100644 index 207bfdf..0000000 --- a/java/src/org/apache/log4j/helpers/Loader.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import java.net.URL; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; -import java.io.InterruptedIOException; - - -/** - Load resources (or images) from various sources. - - @author Ceki Gülcü - */ - -public class Loader { - - static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous."; - - // We conservatively assume that we are running under Java 1.x - static private boolean java1 = true; - - static private boolean ignoreTCL = false; - - static { - String prop = OptionConverter.getSystemProperty("java.version", null); - - if(prop != null) { - int i = prop.indexOf('.'); - if(i != -1) { - if(prop.charAt(i+1) != '1') - java1 = false; - } - } - String ignoreTCLProp = OptionConverter.getSystemProperty("log4j.ignoreTCL", null); - if(ignoreTCLProp != null) { - ignoreTCL = OptionConverter.toBoolean(ignoreTCLProp, true); - } - } - - /** - * Get a resource by delegating to getResource(String). - * @param resource resource name - * @param clazz class, ignored. - * @return URL to resource or null. - * @deprecated as of 1.2. - */ - public static URL getResource(String resource, Class clazz) { - return getResource(resource); - } - - /** - This method will search for resource in different - places. The search order is as follows: - -

    - -

  1. Search for resource using the thread context - class loader under Java2. If that fails, search for - resource using the class loader that loaded this - class (Loader). Under JDK 1.1, only the the class - loader that loaded this class (Loader) is used. - -

  2. Try one last time with - ClassLoader.getSystemResource(resource), that is is - using the system class loader in JDK 1.2 and virtual machine's - built-in class loader in JDK 1.1. - -
- */ - static public URL getResource(String resource) { - ClassLoader classLoader = null; - URL url = null; - - try { - if(!java1 && !ignoreTCL) { - classLoader = getTCL(); - if(classLoader != null) { - LogLog.debug("Trying to find ["+resource+"] using context classloader " - +classLoader+"."); - url = classLoader.getResource(resource); - if(url != null) { - return url; - } - } - } - - // We could not find resource. Ler us now try with the - // classloader that loaded this class. - classLoader = Loader.class.getClassLoader(); - if(classLoader != null) { - LogLog.debug("Trying to find ["+resource+"] using "+classLoader - +" class loader."); - url = classLoader.getResource(resource); - if(url != null) { - return url; - } - } - } catch(IllegalAccessException t) { - LogLog.warn(TSTR, t); - } catch(InvocationTargetException t) { - if (t.getTargetException() instanceof InterruptedException - || t.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.warn(TSTR, t); - } catch(Throwable t) { - // - // can't be InterruptedException or InterruptedIOException - // since not declared, must be error or RuntimeError. - LogLog.warn(TSTR, t); - } - - // Last ditch attempt: get the resource from the class path. It - // may be the case that clazz was loaded by the Extentsion class - // loader which the parent of the system class loader. Hence the - // code below. - LogLog.debug("Trying to find ["+resource+ - "] using ClassLoader.getSystemResource()."); - return ClassLoader.getSystemResource(resource); - } - - /** - Are we running under JDK 1.x? - */ - public - static - boolean isJava1() { - return java1; - } - - /** - * Get the Thread Context Loader which is a JDK 1.2 feature. If we - * are running under JDK 1.1 or anything else goes wrong the method - * returns null. - * - * */ - private static ClassLoader getTCL() throws IllegalAccessException, - InvocationTargetException { - - // Are we running on a JDK 1.2 or later system? - Method method = null; - try { - method = Thread.class.getMethod("getContextClassLoader", null); - } catch (NoSuchMethodException e) { - // We are running on JDK 1.1 - return null; - } - - return (ClassLoader) method.invoke(Thread.currentThread(), null); - } - - - - /** - * If running under JDK 1.2 load the specified class using the - * Thread contextClassLoader if that - * fails try Class.forname. Under JDK 1.1 only Class.forName is - * used. - * - */ - static public Class loadClass (String clazz) throws ClassNotFoundException { - // Just call Class.forName(clazz) if we are running under JDK 1.1 - // or if we are instructed to ignore the TCL. - if(java1 || ignoreTCL) { - return Class.forName(clazz); - } else { - try { - return getTCL().loadClass(clazz); - } - // we reached here because tcl was null or because of a - // security exception, or because clazz could not be loaded... - // In any case we now try one more time - catch(InvocationTargetException e) { - if (e.getTargetException() instanceof InterruptedException - || e.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - } catch(Throwable t) { - } - } - return Class.forName(clazz); - } -} diff --git a/java/src/org/apache/log4j/helpers/LogLog.java b/java/src/org/apache/log4j/helpers/LogLog.java deleted file mode 100644 index a7bd588..0000000 --- a/java/src/org/apache/log4j/helpers/LogLog.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -/** - This class used to output log statements from within the log4j package. - -

Log4j components cannot make log4j logging calls. However, it is - sometimes useful for the user to learn about what log4j is - doing. You can enable log4j internal logging by defining the - log4j.configDebug variable. - -

All log4j internal debug calls go to System.out - where as internal error messages are sent to - System.err. All internal messages are prepended with - the string "log4j: ". - - @since 0.8.2 - @author Ceki Gülcü -*/ -public class LogLog { - - /** - Defining this value makes log4j print log4j-internal debug - statements to System.out. - -

The value of this string is log4j.debug. - -

Note that the search for all option names is case sensitive. */ - public static final String DEBUG_KEY="log4j.debug"; - - - /** - Defining this value makes log4j components print log4j-internal - debug statements to System.out. - -

The value of this string is log4j.configDebug. - -

Note that the search for all option names is case sensitive. - - @deprecated Use {@link #DEBUG_KEY} instead. - */ - public static final String CONFIG_DEBUG_KEY="log4j.configDebug"; - - protected static boolean debugEnabled = false; - - /** - In quietMode not even errors generate any output. - */ - private static boolean quietMode = false; - - private static final String PREFIX = "log4j: "; - private static final String ERR_PREFIX = "log4j:ERROR "; - private static final String WARN_PREFIX = "log4j:WARN "; - - static { - String key = OptionConverter.getSystemProperty(DEBUG_KEY, null); - - if(key == null) { - key = OptionConverter.getSystemProperty(CONFIG_DEBUG_KEY, null); - } - - if(key != null) { - debugEnabled = OptionConverter.toBoolean(key, true); - } - } - - /** - Allows to enable/disable log4j internal logging. - */ - static - public - void setInternalDebugging(boolean enabled) { - debugEnabled = enabled; - } - - /** - This method is used to output log4j internal debug - statements. Output goes to System.out. - */ - public - static - void debug(String msg) { - if(debugEnabled && !quietMode) { - System.out.println(PREFIX+msg); - } - } - - /** - This method is used to output log4j internal debug - statements. Output goes to System.out. - */ - public - static - void debug(String msg, Throwable t) { - if(debugEnabled && !quietMode) { - System.out.println(PREFIX+msg); - if(t != null) - t.printStackTrace(System.out); - } - } - - - /** - This method is used to output log4j internal error - statements. There is no way to disable error statements. - Output goes to System.err. - */ - public - static - void error(String msg) { - if(quietMode) - return; - System.err.println(ERR_PREFIX+msg); - } - - /** - This method is used to output log4j internal error - statements. There is no way to disable error statements. - Output goes to System.err. - */ - public - static - void error(String msg, Throwable t) { - if(quietMode) - return; - - System.err.println(ERR_PREFIX+msg); - if(t != null) { - t.printStackTrace(); - } - } - - /** - In quite mode no LogLog generates strictly no output, not even - for errors. - - @param quietMode A true for not - */ - public - static - void setQuietMode(boolean quietMode) { - LogLog.quietMode = quietMode; - } - - /** - This method is used to output log4j internal warning - statements. There is no way to disable warning statements. - Output goes to System.err. */ - public - static - void warn(String msg) { - if(quietMode) - return; - - System.err.println(WARN_PREFIX+msg); - } - - /** - This method is used to output log4j internal warnings. There is - no way to disable warning statements. Output goes to - System.err. */ - public - static - void warn(String msg, Throwable t) { - if(quietMode) - return; - - System.err.println(WARN_PREFIX+msg); - if(t != null) { - t.printStackTrace(); - } - } -} diff --git a/java/src/org/apache/log4j/helpers/MDCKeySetExtractor.java b/java/src/org/apache/log4j/helpers/MDCKeySetExtractor.java deleted file mode 100644 index 2d2a539..0000000 --- a/java/src/org/apache/log4j/helpers/MDCKeySetExtractor.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.helpers; - -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.pattern.LogEvent; - -import java.lang.reflect.Method; -import java.util.Set; -import java.io.ByteArrayOutputStream; -import java.io.ObjectOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ObjectInputStream; - - -public final class MDCKeySetExtractor { - private final Method getKeySetMethod; - public static final MDCKeySetExtractor INSTANCE = - new MDCKeySetExtractor(); - - - private MDCKeySetExtractor() { - // - // log4j 1.2.15 and later will have method to get names - // of all keys in MDC - // - Method getMethod = null; - - try { - getMethod = LoggingEvent.class.getMethod( - "getPropertyKeySet", null); - } catch(Exception ex) { - getMethod = null; - } - getKeySetMethod = getMethod; - - } - - public Set getPropertyKeySet(final LoggingEvent event) throws Exception { - // - // MDC keys are not visible prior to log4j 1.2.15 - // - Set keySet = null; - if (getKeySetMethod != null) { - keySet = (Set) getKeySetMethod.invoke(event, null); - } else { - // - // for 1.2.14 and earlier could serialize and - // extract MDC content - ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); - ObjectOutputStream os = new ObjectOutputStream(outBytes); - os.writeObject(event); - os.close(); - - byte[] raw = outBytes.toByteArray(); - // - // bytes 6 and 7 should be the length of the original classname - // should be the same as our substitute class name - final String subClassName = LogEvent.class.getName(); - if (raw[6] == 0 || raw[7] == subClassName.length()) { - // - // manipulate stream to use our class name - // - for (int i = 0; i < subClassName.length(); i++) { - raw[8 + i] = (byte) subClassName.charAt(i); - } - ByteArrayInputStream inBytes = new ByteArrayInputStream(raw); - ObjectInputStream is = new ObjectInputStream(inBytes); - Object cracked = is.readObject(); - if (cracked instanceof LogEvent) { - keySet = ((LogEvent) cracked).getPropertyKeySet(); - } - is.close(); - } - } - return keySet; - } -} diff --git a/java/src/org/apache/log4j/helpers/NullEnumeration.java b/java/src/org/apache/log4j/helpers/NullEnumeration.java deleted file mode 100644 index 0f4310d..0000000 --- a/java/src/org/apache/log4j/helpers/NullEnumeration.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import java.util.Enumeration; -import java.util.NoSuchElementException; - -/** - - An always-empty Enumerator. - - @author Anders Kristensen - @since version 1.0 - */ -public class NullEnumeration implements Enumeration { - private static final NullEnumeration instance = new NullEnumeration(); - - private - NullEnumeration() { - } - - public static NullEnumeration getInstance() { - return instance; - } - - public - boolean hasMoreElements() { - return false; - } - - public - Object nextElement() { - throw new NoSuchElementException(); - } -} diff --git a/java/src/org/apache/log4j/helpers/OnlyOnceErrorHandler.java b/java/src/org/apache/log4j/helpers/OnlyOnceErrorHandler.java deleted file mode 100644 index 950778d..0000000 --- a/java/src/org/apache/log4j/helpers/OnlyOnceErrorHandler.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import org.apache.log4j.spi.ErrorHandler; -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.Logger; -import org.apache.log4j.Appender; - -import java.io.InterruptedIOException; - -/** - - The OnlyOnceErrorHandler implements log4j's default - error handling policy which consists of emitting a message for the - first error in an appender and ignoring all following errors. - -

The error message is printed on System.err. - -

This policy aims at protecting an otherwise working application - from being flooded with error messages when logging fails. - - @author Ceki Gülcü - @since 0.9.0 */ -public class OnlyOnceErrorHandler implements ErrorHandler { - - - final String WARN_PREFIX = "log4j warning: "; - final String ERROR_PREFIX = "log4j error: "; - - boolean firstTime = true; - - - /** - Does not do anything. - */ - public - void setLogger(Logger logger) { - } - - - /** - No options to activate. - */ - public - void activateOptions() { - } - - - /** - Prints the message and the stack trace of the exception on - System.err. */ - public - void error(String message, Exception e, int errorCode) { - error(message, e, errorCode, null); - } - - /** - Prints the message and the stack trace of the exception on - System.err. - */ - public - void error(String message, Exception e, int errorCode, LoggingEvent event) { - if (e instanceof InterruptedIOException || e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - if(firstTime) { - LogLog.error(message, e); - firstTime = false; - } - } - - - /** - Print a the error message passed as parameter on - System.err. - */ - public - void error(String message) { - if(firstTime) { - LogLog.error(message); - firstTime = false; - } - } - - /** - Does not do anything. - */ - public - void setAppender(Appender appender) { - } - - /** - Does not do anything. - */ - public - void setBackupAppender(Appender appender) { - } -} diff --git a/java/src/org/apache/log4j/helpers/OptionConverter.java b/java/src/org/apache/log4j/helpers/OptionConverter.java deleted file mode 100644 index 9291774..0000000 --- a/java/src/org/apache/log4j/helpers/OptionConverter.java +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import java.util.Properties; -import java.net.URL; -import java.io.InterruptedIOException; - -import org.apache.log4j.Level; -import org.apache.log4j.spi.Configurator; -import org.apache.log4j.spi.LoggerRepository; -import org.apache.log4j.PropertyConfigurator; - -// Contributors: Avy Sharell (sharell@online.fr) -// Matthieu Verbert (mve@zurich.ibm.com) -// Colin Sampaleanu - -/** - A convenience class to convert property values to specific types. - - @author Ceki Gülcü - @author Simon Kitching; - @author Anders Kristensen -*/ -public class OptionConverter { - - static String DELIM_START = "${"; - static char DELIM_STOP = '}'; - static int DELIM_START_LEN = 2; - static int DELIM_STOP_LEN = 1; - - /** OptionConverter is a static class. */ - private OptionConverter() {} - - public - static - String[] concatanateArrays(String[] l, String[] r) { - int len = l.length + r.length; - String[] a = new String[len]; - - System.arraycopy(l, 0, a, 0, l.length); - System.arraycopy(r, 0, a, l.length, r.length); - - return a; - } - - public - static - String convertSpecialChars(String s) { - char c; - int len = s.length(); - StringBuffer sbuf = new StringBuffer(len); - - int i = 0; - while(i < len) { - c = s.charAt(i++); - if (c == '\\') { - c = s.charAt(i++); - if(c == 'n') c = '\n'; - else if(c == 'r') c = '\r'; - else if(c == 't') c = '\t'; - else if(c == 'f') c = '\f'; - else if(c == '\b') c = '\b'; - else if(c == '\"') c = '\"'; - else if(c == '\'') c = '\''; - else if(c == '\\') c = '\\'; - } - sbuf.append(c); - } - return sbuf.toString(); - } - - - /** - Very similar to System.getProperty except - that the {@link SecurityException} is hidden. - - @param key The key to search for. - @param def The default value to return. - @return the string value of the system property, or the default - value if there is no property with that key. - - @since 1.1 */ - public - static - String getSystemProperty(String key, String def) { - try { - return System.getProperty(key, def); - } catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx - LogLog.debug("Was not allowed to read system property \""+key+"\"."); - return def; - } - } - - - public - static - Object instantiateByKey(Properties props, String key, Class superClass, - Object defaultValue) { - - // Get the value of the property in string form - String className = findAndSubst(key, props); - if(className == null) { - LogLog.error("Could not find value for key " + key); - return defaultValue; - } - // Trim className to avoid trailing spaces that cause problems. - return OptionConverter.instantiateByClassName(className.trim(), superClass, - defaultValue); - } - - /** - If value is "true", then true is - returned. If value is "false", then - true is returned. Otherwise, default is - returned. - -

Case of value is unimportant. */ - public - static - boolean toBoolean(String value, boolean dEfault) { - if(value == null) - return dEfault; - String trimmedVal = value.trim(); - if("true".equalsIgnoreCase(trimmedVal)) - return true; - if("false".equalsIgnoreCase(trimmedVal)) - return false; - return dEfault; - } - - public - static - int toInt(String value, int dEfault) { - if(value != null) { - String s = value.trim(); - try { - return Integer.valueOf(s).intValue(); - } - catch (NumberFormatException e) { - LogLog.error("[" + s + "] is not in proper int form."); - e.printStackTrace(); - } - } - return dEfault; - } - - /** - Converts a standard or custom priority level to a Level - object.

If value is of form - "level#classname", then the specified class' toLevel method - is called to process the specified level string; if no '#' - character is present, then the default {@link org.apache.log4j.Level} - class is used to process the level value. - -

As a special case, if the value parameter is - equal to the string "NULL", then the value null will - be returned. - -

If any error occurs while converting the value to a level, - the defaultValue parameter, which may be - null, is returned. - -

Case of value is insignificant for the level level, but is - significant for the class name part, if present. - - @since 1.1 */ - public - static - Level toLevel(String value, Level defaultValue) { - if(value == null) - return defaultValue; - - value = value.trim(); - - int hashIndex = value.indexOf('#'); - if (hashIndex == -1) { - if("NULL".equalsIgnoreCase(value)) { - return null; - } else { - // no class name specified : use standard Level class - return(Level) Level.toLevel(value, defaultValue); - } - } - - Level result = defaultValue; - - String clazz = value.substring(hashIndex+1); - String levelName = value.substring(0, hashIndex); - - // This is degenerate case but you never know. - if("NULL".equalsIgnoreCase(levelName)) { - return null; - } - - LogLog.debug("toLevel" + ":class=[" + clazz + "]" - + ":pri=[" + levelName + "]"); - - try { - Class customLevel = Loader.loadClass(clazz); - - // get a ref to the specified class' static method - // toLevel(String, org.apache.log4j.Level) - Class[] paramTypes = new Class[] { String.class, - org.apache.log4j.Level.class - }; - java.lang.reflect.Method toLevelMethod = - customLevel.getMethod("toLevel", paramTypes); - - // now call the toLevel method, passing level string + default - Object[] params = new Object[] {levelName, defaultValue}; - Object o = toLevelMethod.invoke(null, params); - - result = (Level) o; - } catch(ClassNotFoundException e) { - LogLog.warn("custom level class [" + clazz + "] not found."); - } catch(NoSuchMethodException e) { - LogLog.warn("custom level class [" + clazz + "]" - + " does not have a class function toLevel(String, Level)", e); - } catch(java.lang.reflect.InvocationTargetException e) { - if (e.getTargetException() instanceof InterruptedException - || e.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.warn("custom level class [" + clazz + "]" - + " could not be instantiated", e); - } catch(ClassCastException e) { - LogLog.warn("class [" + clazz - + "] is not a subclass of org.apache.log4j.Level", e); - } catch(IllegalAccessException e) { - LogLog.warn("class ["+clazz+ - "] cannot be instantiated due to access restrictions", e); - } catch(RuntimeException e) { - LogLog.warn("class ["+clazz+"], level ["+levelName+ - "] conversion failed.", e); - } - return result; - } - - public - static - long toFileSize(String value, long dEfault) { - if(value == null) - return dEfault; - - String s = value.trim().toUpperCase(); - long multiplier = 1; - int index; - - if((index = s.indexOf("KB")) != -1) { - multiplier = 1024; - s = s.substring(0, index); - } - else if((index = s.indexOf("MB")) != -1) { - multiplier = 1024*1024; - s = s.substring(0, index); - } - else if((index = s.indexOf("GB")) != -1) { - multiplier = 1024*1024*1024; - s = s.substring(0, index); - } - if(s != null) { - try { - return Long.valueOf(s).longValue() * multiplier; - } - catch (NumberFormatException e) { - LogLog.error("[" + s + "] is not in proper int form."); - LogLog.error("[" + value + "] not in expected format.", e); - } - } - return dEfault; - } - - /** - Find the value corresponding to key in - props. Then perform variable substitution on the - found value. - - */ - public - static - String findAndSubst(String key, Properties props) { - String value = props.getProperty(key); - if(value == null) - return null; - - try { - return substVars(value, props); - } catch(IllegalArgumentException e) { - LogLog.error("Bad option value ["+value+"].", e); - return value; - } - } - - /** - Instantiate an object given a class name. Check that the - className is a subclass of - superClass. If that test fails or the object could - not be instantiated, then defaultValue is returned. - - @param className The fully qualified class name of the object to instantiate. - @param superClass The class to which the new object should belong. - @param defaultValue The object to return in case of non-fulfillment - */ - public - static - Object instantiateByClassName(String className, Class superClass, - Object defaultValue) { - if(className != null) { - try { - Class classObj = Loader.loadClass(className); - if(!superClass.isAssignableFrom(classObj)) { - LogLog.error("A \""+className+"\" object is not assignable to a \""+ - superClass.getName() + "\" variable."); - LogLog.error("The class \""+ superClass.getName()+"\" was loaded by "); - LogLog.error("["+superClass.getClassLoader()+"] whereas object of type "); - LogLog.error("\"" +classObj.getName()+"\" was loaded by [" - +classObj.getClassLoader()+"]."); - return defaultValue; - } - return classObj.newInstance(); - } catch (ClassNotFoundException e) { - LogLog.error("Could not instantiate class [" + className + "].", e); - } catch (IllegalAccessException e) { - LogLog.error("Could not instantiate class [" + className + "].", e); - } catch (InstantiationException e) { - LogLog.error("Could not instantiate class [" + className + "].", e); - } catch (RuntimeException e) { - LogLog.error("Could not instantiate class [" + className + "].", e); - } - } - return defaultValue; - } - - - /** - Perform variable substitution in string val from the - values of keys found in the system propeties. - -

The variable substitution delimeters are ${ and }. - -

For example, if the System properties contains "key=value", then - the call -

-     String s = OptionConverter.substituteVars("Value of key is ${key}.");
-     
- - will set the variable s to "Value of key is value.". - -

If no value could be found for the specified key, then the - props parameter is searched, if the value could not - be found there, then substitution defaults to the empty string. - -

For example, if system propeties contains no value for the key - "inexistentKey", then the call - -

-     String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
-     
- will set s to "Value of inexistentKey is []" - -

An {@link java.lang.IllegalArgumentException} is thrown if - val contains a start delimeter "${" which is not - balanced by a stop delimeter "}".

- -

Author Avy Sharell

- - @param val The string on which variable substitution is performed. - @throws IllegalArgumentException if val is malformed. - - */ - public static - String substVars(String val, Properties props) throws - IllegalArgumentException { - - StringBuffer sbuf = new StringBuffer(); - - int i = 0; - int j, k; - - while(true) { - j=val.indexOf(DELIM_START, i); - if(j == -1) { - // no more variables - if(i==0) { // this is a simple string - return val; - } else { // add the tail string which contails no variables and return the result. - sbuf.append(val.substring(i, val.length())); - return sbuf.toString(); - } - } else { - sbuf.append(val.substring(i, j)); - k = val.indexOf(DELIM_STOP, j); - if(k == -1) { - throw new IllegalArgumentException('"'+val+ - "\" has no closing brace. Opening brace at position " + j - + '.'); - } else { - j += DELIM_START_LEN; - String key = val.substring(j, k); - // first try in System properties - String replacement = getSystemProperty(key, null); - // then try props parameter - if(replacement == null && props != null) { - replacement = props.getProperty(key); - } - - if(replacement != null) { - // Do variable substitution on the replacement string - // such that we can solve "Hello ${x2}" as "Hello p1" - // the where the properties are - // x1=p1 - // x2=${x1} - String recursiveReplacement = substVars(replacement, props); - sbuf.append(recursiveReplacement); - } - i = k + DELIM_STOP_LEN; - } - } - } - } - - - /** - Configure log4j given a URL. - -

The url must point to a file or resource which will be interpreted by - a new instance of a log4j configurator. - -

All configurations steps are taken on the - hierarchy passed as a parameter. - -

- @param url The location of the configuration file or resource. - @param clazz The classname, of the log4j configurator which will parse - the file or resource at url. This must be a subclass of - {@link Configurator}, or null. If this value is null then a default - configurator of {@link PropertyConfigurator} is used, unless the - filename pointed to by url ends in '.xml', in which case - {@link org.apache.log4j.xml.DOMConfigurator} is used. - @param hierarchy The {@link org.apache.log4j.Hierarchy} to act on. - - @since 1.1.4 */ - - static - public - void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) { - Configurator configurator = null; - String filename = url.getFile(); - - if(clazz == null && filename != null && filename.endsWith(".xml")) { - clazz = "org.apache.log4j.xml.DOMConfigurator"; - } - - if(clazz != null) { - LogLog.debug("Preferred configurator class: " + clazz); - configurator = (Configurator) instantiateByClassName(clazz, - Configurator.class, - null); - if(configurator == null) { - LogLog.error("Could not instantiate configurator ["+clazz+"]."); - return; - } - } else { - configurator = new PropertyConfigurator(); - } - - configurator.doConfigure(url, hierarchy); - } -} diff --git a/java/src/org/apache/log4j/helpers/PatternConverter.java b/java/src/org/apache/log4j/helpers/PatternConverter.java deleted file mode 100644 index 2b46db7..0000000 --- a/java/src/org/apache/log4j/helpers/PatternConverter.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import org.apache.log4j.spi.LoggingEvent; - -/** - -

PatternConverter is an abtract class that provides the - formatting functionality that derived classes need. - -

Conversion specifiers in a conversion patterns are parsed to - individual PatternConverters. Each of which is responsible for - converting a logging event in a converter specific manner. - - @author James P. Cakalic - @author Ceki Gülcü - - @since 0.8.2 - */ -public abstract class PatternConverter { - public PatternConverter next; - int min = -1; - int max = 0x7FFFFFFF; - boolean leftAlign = false; - - protected - PatternConverter() { } - - protected - PatternConverter(FormattingInfo fi) { - min = fi.min; - max = fi.max; - leftAlign = fi.leftAlign; - } - - /** - Derived pattern converters must override this method in order to - convert conversion specifiers in the correct way. - */ - abstract - protected - String convert(LoggingEvent event); - - /** - A template method for formatting in a converter specific way. - */ - public - void format(StringBuffer sbuf, LoggingEvent e) { - String s = convert(e); - - if(s == null) { - if(0 < min) - spacePad(sbuf, min); - return; - } - - int len = s.length(); - - if(len > max) - sbuf.append(s.substring(len-max)); - else if(len < min) { - if(leftAlign) { - sbuf.append(s); - spacePad(sbuf, min-len); - } - else { - spacePad(sbuf, min-len); - sbuf.append(s); - } - } - else - sbuf.append(s); - } - - static String[] SPACES = {" ", " ", " ", " ", //1,2,4,8 spaces - " ", // 16 spaces - " " }; // 32 spaces - - /** - Fast space padding method. - */ - public - void spacePad(StringBuffer sbuf, int length) { - while(length >= 32) { - sbuf.append(SPACES[5]); - length -= 32; - } - - for(int i = 4; i >= 0; i--) { - if((length & (1< -// Igor E. Poteryaev -// Reinhard Deschler - -/** - Most of the work of the {@link org.apache.log4j.PatternLayout} class - is delegated to the PatternParser class. - -

It is this class that parses conversion patterns and creates - a chained list of {@link OptionConverter OptionConverters}. - - @author James P. Cakalic - @author Ceki Gülcü - @author Anders Kristensen - - @since 0.8.2 -*/ -public class PatternParser { - - private static final char ESCAPE_CHAR = '%'; - - private static final int LITERAL_STATE = 0; - private static final int CONVERTER_STATE = 1; - private static final int DOT_STATE = 3; - private static final int MIN_STATE = 4; - private static final int MAX_STATE = 5; - - static final int FULL_LOCATION_CONVERTER = 1000; - static final int METHOD_LOCATION_CONVERTER = 1001; - static final int CLASS_LOCATION_CONVERTER = 1002; - static final int LINE_LOCATION_CONVERTER = 1003; - static final int FILE_LOCATION_CONVERTER = 1004; - - static final int RELATIVE_TIME_CONVERTER = 2000; - static final int THREAD_CONVERTER = 2001; - static final int LEVEL_CONVERTER = 2002; - static final int NDC_CONVERTER = 2003; - static final int MESSAGE_CONVERTER = 2004; - - int state; - protected StringBuffer currentLiteral = new StringBuffer(32); - protected int patternLength; - protected int i; - PatternConverter head; - PatternConverter tail; - protected FormattingInfo formattingInfo = new FormattingInfo(); - protected String pattern; - - public - PatternParser(String pattern) { - this.pattern = pattern; - patternLength = pattern.length(); - state = LITERAL_STATE; - } - - private - void addToList(PatternConverter pc) { - if(head == null) { - head = tail = pc; - } else { - tail.next = pc; - tail = pc; - } - } - - protected - String extractOption() { - if((i < patternLength) && (pattern.charAt(i) == '{')) { - int end = pattern.indexOf('}', i); - if (end > i) { - String r = pattern.substring(i + 1, end); - i = end+1; - return r; - } - } - return null; - } - - - /** - The option is expected to be in decimal and positive. In case of - error, zero is returned. */ - protected - int extractPrecisionOption() { - String opt = extractOption(); - int r = 0; - if(opt != null) { - try { - r = Integer.parseInt(opt); - if(r <= 0) { - LogLog.error( - "Precision option (" + opt + ") isn't a positive integer."); - r = 0; - } - } - catch (NumberFormatException e) { - LogLog.error("Category option \""+opt+"\" not a decimal integer.", e); - } - } - return r; - } - - public - PatternConverter parse() { - char c; - i = 0; - while(i < patternLength) { - c = pattern.charAt(i++); - switch(state) { - case LITERAL_STATE: - // In literal state, the last char is always a literal. - if(i == patternLength) { - currentLiteral.append(c); - continue; - } - if(c == ESCAPE_CHAR) { - // peek at the next char. - switch(pattern.charAt(i)) { - case ESCAPE_CHAR: - currentLiteral.append(c); - i++; // move pointer - break; - case 'n': - currentLiteral.append(Layout.LINE_SEP); - i++; // move pointer - break; - default: - if(currentLiteral.length() != 0) { - addToList(new LiteralPatternConverter( - currentLiteral.toString())); - //LogLog.debug("Parsed LITERAL converter: \"" - // +currentLiteral+"\"."); - } - currentLiteral.setLength(0); - currentLiteral.append(c); // append % - state = CONVERTER_STATE; - formattingInfo.reset(); - } - } - else { - currentLiteral.append(c); - } - break; - case CONVERTER_STATE: - currentLiteral.append(c); - switch(c) { - case '-': - formattingInfo.leftAlign = true; - break; - case '.': - state = DOT_STATE; - break; - default: - if(c >= '0' && c <= '9') { - formattingInfo.min = c - '0'; - state = MIN_STATE; - } - else - finalizeConverter(c); - } // switch - break; - case MIN_STATE: - currentLiteral.append(c); - if(c >= '0' && c <= '9') - formattingInfo.min = formattingInfo.min*10 + (c - '0'); - else if(c == '.') - state = DOT_STATE; - else { - finalizeConverter(c); - } - break; - case DOT_STATE: - currentLiteral.append(c); - if(c >= '0' && c <= '9') { - formattingInfo.max = c - '0'; - state = MAX_STATE; - } - else { - LogLog.error("Error occured in position "+i - +".\n Was expecting digit, instead got char \""+c+"\"."); - state = LITERAL_STATE; - } - break; - case MAX_STATE: - currentLiteral.append(c); - if(c >= '0' && c <= '9') - formattingInfo.max = formattingInfo.max*10 + (c - '0'); - else { - finalizeConverter(c); - state = LITERAL_STATE; - } - break; - } // switch - } // while - if(currentLiteral.length() != 0) { - addToList(new LiteralPatternConverter(currentLiteral.toString())); - //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\"."); - } - return head; - } - - protected - void finalizeConverter(char c) { - PatternConverter pc = null; - switch(c) { - case 'c': - pc = new CategoryPatternConverter(formattingInfo, - extractPrecisionOption()); - //LogLog.debug("CATEGORY converter."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - case 'C': - pc = new ClassNamePatternConverter(formattingInfo, - extractPrecisionOption()); - //LogLog.debug("CLASS_NAME converter."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - case 'd': - String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT; - DateFormat df; - String dOpt = extractOption(); - if(dOpt != null) - dateFormatStr = dOpt; - - if(dateFormatStr.equalsIgnoreCase( - AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT)) - df = new ISO8601DateFormat(); - else if(dateFormatStr.equalsIgnoreCase( - AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT)) - df = new AbsoluteTimeDateFormat(); - else if(dateFormatStr.equalsIgnoreCase( - AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT)) - df = new DateTimeDateFormat(); - else { - try { - df = new SimpleDateFormat(dateFormatStr); - } - catch (IllegalArgumentException e) { - LogLog.error("Could not instantiate SimpleDateFormat with " + - dateFormatStr, e); - df = (DateFormat) OptionConverter.instantiateByClassName( - "org.apache.log4j.helpers.ISO8601DateFormat", - DateFormat.class, null); - } - } - pc = new DatePatternConverter(formattingInfo, df); - //LogLog.debug("DATE converter {"+dateFormatStr+"}."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - case 'F': - pc = new LocationPatternConverter(formattingInfo, - FILE_LOCATION_CONVERTER); - //LogLog.debug("File name converter."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - case 'l': - pc = new LocationPatternConverter(formattingInfo, - FULL_LOCATION_CONVERTER); - //LogLog.debug("Location converter."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - case 'L': - pc = new LocationPatternConverter(formattingInfo, - LINE_LOCATION_CONVERTER); - //LogLog.debug("LINE NUMBER converter."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - case 'm': - pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER); - //LogLog.debug("MESSAGE converter."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - case 'M': - pc = new LocationPatternConverter(formattingInfo, - METHOD_LOCATION_CONVERTER); - //LogLog.debug("METHOD converter."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - case 'p': - pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER); - //LogLog.debug("LEVEL converter."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - case 'r': - pc = new BasicPatternConverter(formattingInfo, - RELATIVE_TIME_CONVERTER); - //LogLog.debug("RELATIVE time converter."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - case 't': - pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER); - //LogLog.debug("THREAD converter."); - //formattingInfo.dump(); - currentLiteral.setLength(0); - break; - /*case 'u': - if(i < patternLength) { - char cNext = pattern.charAt(i); - if(cNext >= '0' && cNext <= '9') { - pc = new UserFieldPatternConverter(formattingInfo, cNext - '0'); - LogLog.debug("USER converter ["+cNext+"]."); - formattingInfo.dump(); - currentLiteral.setLength(0); - i++; - } - else - LogLog.error("Unexpected char" +cNext+" at position "+i); - } - break;*/ - case 'x': - pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER); - //LogLog.debug("NDC converter."); - currentLiteral.setLength(0); - break; - case 'X': - String xOpt = extractOption(); - pc = new MDCPatternConverter(formattingInfo, xOpt); - currentLiteral.setLength(0); - break; - default: - LogLog.error("Unexpected char [" +c+"] at position "+i - +" in conversion patterrn."); - pc = new LiteralPatternConverter(currentLiteral.toString()); - currentLiteral.setLength(0); - } - - addConverter(pc); - } - - protected - void addConverter(PatternConverter pc) { - currentLiteral.setLength(0); - // Add the pattern converter to the list. - addToList(pc); - // Next pattern is assumed to be a literal. - state = LITERAL_STATE; - // Reset formatting info - formattingInfo.reset(); - } - - // --------------------------------------------------------------------- - // PatternConverters - // --------------------------------------------------------------------- - - private static class BasicPatternConverter extends PatternConverter { - int type; - - BasicPatternConverter(FormattingInfo formattingInfo, int type) { - super(formattingInfo); - this.type = type; - } - - public - String convert(LoggingEvent event) { - switch(type) { - case RELATIVE_TIME_CONVERTER: - return (Long.toString(event.timeStamp - LoggingEvent.getStartTime())); - case THREAD_CONVERTER: - return event.getThreadName(); - case LEVEL_CONVERTER: - return event.getLevel().toString(); - case NDC_CONVERTER: - return event.getNDC(); - case MESSAGE_CONVERTER: { - return event.getRenderedMessage(); - } - default: return null; - } - } - } - - private static class LiteralPatternConverter extends PatternConverter { - private String literal; - - LiteralPatternConverter(String value) { - literal = value; - } - - public - final - void format(StringBuffer sbuf, LoggingEvent event) { - sbuf.append(literal); - } - - public - String convert(LoggingEvent event) { - return literal; - } - } - - private static class DatePatternConverter extends PatternConverter { - private DateFormat df; - private Date date; - - DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) { - super(formattingInfo); - date = new Date(); - this.df = df; - } - - public - String convert(LoggingEvent event) { - date.setTime(event.timeStamp); - String converted = null; - try { - converted = df.format(date); - } - catch (Exception ex) { - LogLog.error("Error occured while converting date.", ex); - } - return converted; - } - } - - private static class MDCPatternConverter extends PatternConverter { - private String key; - - MDCPatternConverter(FormattingInfo formattingInfo, String key) { - super(formattingInfo); - this.key = key; - } - - public - String convert(LoggingEvent event) { - if (key == null) { - StringBuffer buf = new StringBuffer("{"); - Map properties = event.getProperties(); - if (properties.size() > 0) { - Object[] keys = properties.keySet().toArray(); - Arrays.sort(keys); - for (int i = 0; i < keys.length; i++) { - buf.append('{'); - buf.append(keys[i]); - buf.append(','); - buf.append(properties.get(keys[i])); - buf.append('}'); - } - } - buf.append('}'); - return buf.toString(); - } else { - Object val = event.getMDC(key); - if(val == null) { - return null; - } else { - return val.toString(); - } - } - } - } - - - private class LocationPatternConverter extends PatternConverter { - int type; - - LocationPatternConverter(FormattingInfo formattingInfo, int type) { - super(formattingInfo); - this.type = type; - } - - public - String convert(LoggingEvent event) { - LocationInfo locationInfo = event.getLocationInformation(); - switch(type) { - case FULL_LOCATION_CONVERTER: - return locationInfo.fullInfo; - case METHOD_LOCATION_CONVERTER: - return locationInfo.getMethodName(); - case LINE_LOCATION_CONVERTER: - return locationInfo.getLineNumber(); - case FILE_LOCATION_CONVERTER: - return locationInfo.getFileName(); - default: return null; - } - } - } - - private static abstract class NamedPatternConverter extends PatternConverter { - int precision; - - NamedPatternConverter(FormattingInfo formattingInfo, int precision) { - super(formattingInfo); - this.precision = precision; - } - - abstract - String getFullyQualifiedName(LoggingEvent event); - - public - String convert(LoggingEvent event) { - String n = getFullyQualifiedName(event); - if(precision <= 0) - return n; - else { - int len = n.length(); - - // We substract 1 from 'len' when assigning to 'end' to avoid out of - // bounds exception in return r.substring(end+1, len). This can happen if - // precision is 1 and the category name ends with a dot. - int end = len -1 ; - for(int i = precision; i > 0; i--) { - end = n.lastIndexOf('.', end-1); - if(end == -1) - return n; - } - return n.substring(end+1, len); - } - } - } - - private class ClassNamePatternConverter extends NamedPatternConverter { - - ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) { - super(formattingInfo, precision); - } - - String getFullyQualifiedName(LoggingEvent event) { - return event.getLocationInformation().getClassName(); - } - } - - private class CategoryPatternConverter extends NamedPatternConverter { - - CategoryPatternConverter(FormattingInfo formattingInfo, int precision) { - super(formattingInfo, precision); - } - - String getFullyQualifiedName(LoggingEvent event) { - return event.getLoggerName(); - } - } -} - diff --git a/java/src/org/apache/log4j/helpers/QuietWriter.java b/java/src/org/apache/log4j/helpers/QuietWriter.java deleted file mode 100644 index 778f091..0000000 --- a/java/src/org/apache/log4j/helpers/QuietWriter.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import java.io.Writer; -import java.io.FilterWriter; -import org.apache.log4j.spi.ErrorHandler; -import org.apache.log4j.spi.ErrorCode; - - -/** - QuietWriter does not throw exceptions when things go - wrong. Instead, it delegates error handling to its {@link ErrorHandler}. - - @author Ceki Gülcü - - @since 0.7.3 -*/ -public class QuietWriter extends FilterWriter { - - protected ErrorHandler errorHandler; - - public - QuietWriter(Writer writer, ErrorHandler errorHandler) { - super(writer); - setErrorHandler(errorHandler); - } - - public - void write(String string) { - if (string != null) { - try { - out.write(string); - } catch(Exception e) { - errorHandler.error("Failed to write ["+string+"].", e, - ErrorCode.WRITE_FAILURE); - } - } - } - - public - void flush() { - try { - out.flush(); - } catch(Exception e) { - errorHandler.error("Failed to flush writer,", e, - ErrorCode.FLUSH_FAILURE); - } - } - - - public - void setErrorHandler(ErrorHandler eh) { - if(eh == null) { - // This is a programming error on the part of the enclosing appender. - throw new IllegalArgumentException("Attempted to set null ErrorHandler."); - } else { - this.errorHandler = eh; - } - } -} diff --git a/java/src/org/apache/log4j/helpers/RelativeTimeDateFormat.java b/java/src/org/apache/log4j/helpers/RelativeTimeDateFormat.java deleted file mode 100644 index ab81a34..0000000 --- a/java/src/org/apache/log4j/helpers/RelativeTimeDateFormat.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import java.util.Date; -import java.text.FieldPosition; -import java.text.ParsePosition; -import java.text.DateFormat; - -/** - Formats a {@link Date} by printing the number of milliseconds - elapsed since construction of the format. This is the fastest - printing DateFormat in the package. - - @author Ceki Gülcü - - @since 0.7.5 -*/ -public class RelativeTimeDateFormat extends DateFormat { - private static final long serialVersionUID = 7055751607085611984L; - - - protected final long startTime; - - public - RelativeTimeDateFormat() { - this.startTime = System.currentTimeMillis(); - } - - /** - Appends to sbuf the number of milliseconds elapsed - since the start of the application. - - @since 0.7.5 - */ - public - StringBuffer format(Date date, StringBuffer sbuf, - FieldPosition fieldPosition) { - //System.err.println(":"+ date.getTime() + " - " + startTime); - return sbuf.append((date.getTime() - startTime)); - } - - /** - This method does not do anything but return null. - */ - public - Date parse(java.lang.String s, ParsePosition pos) { - return null; - } -} diff --git a/java/src/org/apache/log4j/helpers/SyslogQuietWriter.java b/java/src/org/apache/log4j/helpers/SyslogQuietWriter.java deleted file mode 100644 index 62e933e..0000000 --- a/java/src/org/apache/log4j/helpers/SyslogQuietWriter.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - - - -import java.io.Writer; -import org.apache.log4j.spi.ErrorHandler; - -/** - SyslogQuietWriter extends QuietWriter by prepending the syslog - level code before each printed String. - - @since 0.7.3 -*/ -public class SyslogQuietWriter extends QuietWriter { - - int syslogFacility; - int level; - - public - SyslogQuietWriter(Writer writer, int syslogFacility, ErrorHandler eh) { - super(writer, eh); - this.syslogFacility = syslogFacility; - } - - public - void setLevel(int level) { - this.level = level; - } - - public - void setSyslogFacility(int syslogFacility) { - this.syslogFacility = syslogFacility; - } - - public - void write(String string) { - super.write("<"+(syslogFacility | level)+">" + string); - } -} diff --git a/java/src/org/apache/log4j/helpers/SyslogWriter.java b/java/src/org/apache/log4j/helpers/SyslogWriter.java deleted file mode 100644 index d6ce4bf..0000000 --- a/java/src/org/apache/log4j/helpers/SyslogWriter.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - - -import java.io.Writer; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.DatagramPacket; -import java.net.UnknownHostException; -import java.net.SocketException; -import java.io.IOException; -import java.net.URL; -import java.net.MalformedURLException; - -/** - SyslogWriter is a wrapper around the java.net.DatagramSocket class - so that it behaves like a java.io.Writer. - - @since 0.7.3 -*/ -public class SyslogWriter extends Writer { - - final int SYSLOG_PORT = 514; - /** - * Host string from last constructed SyslogWriter. - * @deprecated - */ - static String syslogHost; - - private InetAddress address; - private final int port; - private DatagramSocket ds; - - /** - * Constructs a new instance of SyslogWriter. - * @param syslogHost host name, may not be null. A port - * may be specified by following the name or IPv4 literal address with - * a colon and a decimal port number. To specify a port with an IPv6 - * address, enclose the IPv6 address in square brackets before appending - * the colon and decimal port number. - */ - public - SyslogWriter(final String syslogHost) { - SyslogWriter.syslogHost = syslogHost; - if (syslogHost == null) { - throw new NullPointerException("syslogHost"); - } - - String host = syslogHost; - int urlPort = -1; - - // - // If not an unbracketed IPv6 address then - // parse as a URL - // - if (host.indexOf("[") != -1 || host.indexOf(':') == host.lastIndexOf(':')) { - try { - URL url = new URL("http://" + host); - if (url.getHost() != null) { - host = url.getHost(); - // if host is a IPv6 literal, strip off the brackets - if(host.startsWith("[") && host.charAt(host.length() - 1) == ']') { - host = host.substring(1, host.length() - 1); - } - urlPort = url.getPort(); - } - } catch(MalformedURLException e) { - LogLog.error("Malformed URL: will attempt to interpret as InetAddress.", e); - } - } - - if (urlPort == -1) { - urlPort = SYSLOG_PORT; - } - port = urlPort; - - try { - this.address = InetAddress.getByName(host); - } - catch (UnknownHostException e) { - LogLog.error("Could not find " + host + - ". All logging will FAIL.", e); - } - - try { - this.ds = new DatagramSocket(); - } - catch (SocketException e) { - e.printStackTrace(); - LogLog.error("Could not instantiate DatagramSocket to " + host + - ". All logging will FAIL.", e); - } - - } - - - public - void write(char[] buf, int off, int len) throws IOException { - this.write(new String(buf, off, len)); - } - - public - void write(final String string) throws IOException { - - if(this.ds != null && this.address != null) { - byte[] bytes = string.getBytes(); - // - // syslog packets must be less than 1024 bytes - // - int bytesLength = bytes.length; - if (bytesLength >= 1024) { - bytesLength = 1024; - } - DatagramPacket packet = new DatagramPacket(bytes, bytesLength, - address, port); - ds.send(packet); - } - - } - - public - void flush() {} - - public void close() { - if (ds != null) { - ds.close(); - } - } -} diff --git a/java/src/org/apache/log4j/helpers/ThreadLocalMap.java b/java/src/org/apache/log4j/helpers/ThreadLocalMap.java deleted file mode 100644 index da60c86..0000000 --- a/java/src/org/apache/log4j/helpers/ThreadLocalMap.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -import java.util.Hashtable; - -/** - ThreadLocalMap extends {@link InheritableThreadLocal} - to bequeath a copy of the hashtable of the MDC of the parent - thread. - - @author Ceki Gülcü - @since 1.2 -*/ -final public class ThreadLocalMap extends InheritableThreadLocal { - - public - final - Object childValue(Object parentValue) { - Hashtable ht = (Hashtable) parentValue; - if(ht != null) { - return ht.clone(); - } else { - return null; - } - } -} diff --git a/java/src/org/apache/log4j/helpers/Transform.java b/java/src/org/apache/log4j/helpers/Transform.java deleted file mode 100644 index 7626e71..0000000 --- a/java/src/org/apache/log4j/helpers/Transform.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.helpers; - -/** - Utility class for transforming strings. - - @author Ceki Gülcü - @author Michael A. McAngus - */ -public class Transform { - - private static final String CDATA_START = ""; - private static final String CDATA_PSEUDO_END = "]]>"; - private static final String CDATA_EMBEDED_END = CDATA_END + CDATA_PSEUDO_END + CDATA_START; - private static final int CDATA_END_LEN = CDATA_END.length(); - - /** - * This method takes a string which may contain HTML tags (ie, - * <b>, <table>, etc) and replaces any - * '<', '>' , '&' or '"' - * characters with respective predefined entity references. - * - * @param input The text to be converted. - * @return The input string with the special characters replaced. - * */ - static public String escapeTags(final String input) { - //Check if the string is null, zero length or devoid of special characters - // if so, return what was sent in. - - if(input == null - || input.length() == 0 - || (input.indexOf('"') == -1 && - input.indexOf('&') == -1 && - input.indexOf('<') == -1 && - input.indexOf('>') == -1)) { - return input; - } - - //Use a StringBuffer in lieu of String concatenation -- it is - //much more efficient this way. - - StringBuffer buf = new StringBuffer(input.length() + 6); - char ch = ' '; - - int len = input.length(); - for(int i=0; i < len; i++) { - ch = input.charAt(i); - if (ch > '>') { - buf.append(ch); - } else if(ch == '<') { - buf.append("<"); - } else if(ch == '>') { - buf.append(">"); - } else if(ch == '&') { - buf.append("&"); - } else if(ch == '"') { - buf.append("""); - } else { - buf.append(ch); - } - } - return buf.toString(); - } - - /** - * Ensures that embeded CDEnd strings (]]>) are handled properly - * within message, NDC and throwable tag text. - * - * @param buf StringBuffer holding the XML data to this point. The - * initial CDStart () of the CDATA - * section are the responsibility of the calling method. - * @param str The String that is inserted into an existing CDATA Section within buf. - * */ - static public void appendEscapingCDATA(final StringBuffer buf, - final String str) { - if (str != null) { - int end = str.indexOf(CDATA_END); - if (end < 0) { - buf.append(str); - } else { - int start = 0; - while (end > -1) { - buf.append(str.substring(start, end)); - buf.append(CDATA_EMBEDED_END); - start = end + CDATA_END_LEN; - if (start < str.length()) { - end = str.indexOf(CDATA_END, start); - } else { - return; - } - } - buf.append(str.substring(start)); - } - } - } -} diff --git a/java/src/org/apache/log4j/helpers/package.html b/java/src/org/apache/log4j/helpers/package.html deleted file mode 100644 index ab75bf3..0000000 --- a/java/src/org/apache/log4j/helpers/package.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - -

This package is used internally. - - -


-
- -Last modified: Sat Jul 3 15:12:58 MDT 1999 - - diff --git a/java/src/org/apache/log4j/jdbc/JDBCAppender.java b/java/src/org/apache/log4j/jdbc/JDBCAppender.java deleted file mode 100644 index f18974a..0000000 --- a/java/src/org/apache/log4j/jdbc/JDBCAppender.java +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.jdbc; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Iterator; - -import org.apache.log4j.PatternLayout; -import org.apache.log4j.spi.ErrorCode; -import org.apache.log4j.spi.LoggingEvent; - - -/** - The JDBCAppender provides for sending log events to a database. - -

WARNING: This version of JDBCAppender - is very likely to be completely replaced in the future. Moreoever, - it does not log exceptions. - -

Each append call adds to an ArrayList buffer. When - the buffer is filled each log event is placed in a sql statement - (configurable) and executed. - - BufferSize, db URL, User, & Password are - configurable options in the standard log4j ways. - -

The setSql(String sql) sets the SQL statement to be - used for logging -- this statement is sent to a - PatternLayout (either created automaticly by the - appender or added by the user). Therefore by default all the - conversion patterns in PatternLayout can be used - inside of the statement. (see the test cases for examples) - -

Overriding the {@link #getLogStatement} method allows more - explicit control of the statement used for logging. - -

For use as a base class: - -

    - -
  • Override getConnection() to pass any connection - you want. Typically this is used to enable application wide - connection pooling. - -
  • Override closeConnection(Connection con) -- if - you override getConnection make sure to implement - closeConnection to handle the connection you - generated. Typically this would return the connection to the - pool it came from. - -
  • Override getLogStatement(LoggingEvent event) to - produce specialized or dynamic statements. The default uses the - sql option value. - -
- - @author Kevin Steppe (ksteppe@pacbell.net) - -*/ -public class JDBCAppender extends org.apache.log4j.AppenderSkeleton - implements org.apache.log4j.Appender { - - /** - * URL of the DB for default connection handling - */ - protected String databaseURL = "jdbc:odbc:myDB"; - - /** - * User to connect as for default connection handling - */ - protected String databaseUser = "me"; - - /** - * User to use for default connection handling - */ - protected String databasePassword = "mypassword"; - - /** - * Connection used by default. The connection is opened the first time it - * is needed and then held open until the appender is closed (usually at - * garbage collection). This behavior is best modified by creating a - * sub-class and overriding the getConnection and - * closeConnection methods. - */ - protected Connection connection = null; - - /** - * Stores the string given to the pattern layout for conversion into a SQL - * statement, eg: insert into LogTable (Thread, Class, Message) values - * ("%t", "%c", "%m"). - * - * Be careful of quotes in your messages! - * - * Also see PatternLayout. - */ - protected String sqlStatement = ""; - - /** - * size of LoggingEvent buffer before writting to the database. - * Default is 1. - */ - protected int bufferSize = 1; - - /** - * ArrayList holding the buffer of Logging Events. - */ - protected ArrayList buffer; - - /** - * Helper object for clearing out the buffer - */ - protected ArrayList removes; - - private boolean locationInfo = false; - - public JDBCAppender() { - super(); - buffer = new ArrayList(bufferSize); - removes = new ArrayList(bufferSize); - } - - /** - * Gets whether the location of the logging request call - * should be captured. - * - * @since 1.2.16 - * @return the current value of the LocationInfo option. - */ - public boolean getLocationInfo() { - return locationInfo; - } - - /** - * The LocationInfo option takes a boolean value. By default, it is - * set to false which means there will be no effort to extract the location - * information related to the event. As a result, the event that will be - * ultimately logged will likely to contain the wrong location information - * (if present in the log format). - *

- *

- * Location information extraction is comparatively very slow and should be - * avoided unless performance is not a concern. - *

- * @since 1.2.16 - * @param flag true if location information should be extracted. - */ - public void setLocationInfo(final boolean flag) { - locationInfo = flag; - } - - - /** - * Adds the event to the buffer. When full the buffer is flushed. - */ - public void append(LoggingEvent event) { - event.getNDC(); - event.getThreadName(); - // Get a copy of this thread's MDC. - event.getMDCCopy(); - if (locationInfo) { - event.getLocationInformation(); - } - event.getRenderedMessage(); - event.getThrowableStrRep(); - buffer.add(event); - - if (buffer.size() >= bufferSize) - flushBuffer(); - } - - /** - * By default getLogStatement sends the event to the required Layout object. - * The layout will format the given pattern into a workable SQL string. - * - * Overriding this provides direct access to the LoggingEvent - * when constructing the logging statement. - * - */ - protected String getLogStatement(LoggingEvent event) { - return getLayout().format(event); - } - - /** - * - * Override this to provide an alertnate method of getting - * connections (such as caching). One method to fix this is to open - * connections at the start of flushBuffer() and close them at the - * end. I use a connection pool outside of JDBCAppender which is - * accessed in an override of this method. - * */ - protected void execute(String sql) throws SQLException { - - Connection con = null; - Statement stmt = null; - - try { - con = getConnection(); - - stmt = con.createStatement(); - stmt.executeUpdate(sql); - } catch (SQLException e) { - if (stmt != null) - stmt.close(); - throw e; - } - stmt.close(); - closeConnection(con); - - //System.out.println("Execute: " + sql); - } - - - /** - * Override this to return the connection to a pool, or to clean up the - * resource. - * - * The default behavior holds a single connection open until the appender - * is closed (typically when garbage collected). - */ - protected void closeConnection(Connection con) { - } - - /** - * Override this to link with your connection pooling system. - * - * By default this creates a single connection which is held open - * until the object is garbage collected. - */ - protected Connection getConnection() throws SQLException { - if (!DriverManager.getDrivers().hasMoreElements()) - setDriver("sun.jdbc.odbc.JdbcOdbcDriver"); - - if (connection == null) { - connection = DriverManager.getConnection(databaseURL, databaseUser, - databasePassword); - } - - return connection; - } - - /** - * Closes the appender, flushing the buffer first then closing the default - * connection if it is open. - */ - public void close() - { - flushBuffer(); - - try { - if (connection != null && !connection.isClosed()) - connection.close(); - } catch (SQLException e) { - errorHandler.error("Error closing connection", e, ErrorCode.GENERIC_FAILURE); - } - this.closed = true; - } - - /** - * loops through the buffer of LoggingEvents, gets a - * sql string from getLogStatement() and sends it to execute(). - * Errors are sent to the errorHandler. - * - * If a statement fails the LoggingEvent stays in the buffer! - */ - public void flushBuffer() { - //Do the actual logging - removes.ensureCapacity(buffer.size()); - for (Iterator i = buffer.iterator(); i.hasNext();) { - try { - LoggingEvent logEvent = (LoggingEvent)i.next(); - String sql = getLogStatement(logEvent); - execute(sql); - removes.add(logEvent); - } - catch (SQLException e) { - errorHandler.error("Failed to excute sql", e, - ErrorCode.FLUSH_FAILURE); - } - } - - // remove from the buffer any events that were reported - buffer.removeAll(removes); - - // clear the buffer of reported events - removes.clear(); - } - - - /** closes the appender before disposal */ - public void finalize() { - close(); - } - - - /** - * JDBCAppender requires a layout. - * */ - public boolean requiresLayout() { - return true; - } - - - /** - * - */ - public void setSql(String s) { - sqlStatement = s; - if (getLayout() == null) { - this.setLayout(new PatternLayout(s)); - } - else { - ((PatternLayout)getLayout()).setConversionPattern(s); - } - } - - - /** - * Returns pre-formated statement eg: insert into LogTable (msg) values ("%m") - */ - public String getSql() { - return sqlStatement; - } - - - public void setUser(String user) { - databaseUser = user; - } - - - public void setURL(String url) { - databaseURL = url; - } - - - public void setPassword(String password) { - databasePassword = password; - } - - - public void setBufferSize(int newBufferSize) { - bufferSize = newBufferSize; - buffer.ensureCapacity(bufferSize); - removes.ensureCapacity(bufferSize); - } - - - public String getUser() { - return databaseUser; - } - - - public String getURL() { - return databaseURL; - } - - - public String getPassword() { - return databasePassword; - } - - - public int getBufferSize() { - return bufferSize; - } - - - /** - * Ensures that the given driver class has been loaded for sql connection - * creation. - */ - public void setDriver(String driverClass) { - try { - Class.forName(driverClass); - } catch (Exception e) { - errorHandler.error("Failed to load driver", e, - ErrorCode.GENERIC_FAILURE); - } - } -} - diff --git a/java/src/org/apache/log4j/jdbc/package.html b/java/src/org/apache/log4j/jdbc/package.html deleted file mode 100644 index e57e3a5..0000000 --- a/java/src/org/apache/log4j/jdbc/package.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - The JDBCAppender provides for sending log events to a database. - - \ No newline at end of file diff --git a/java/src/org/apache/log4j/jmx/AbstractDynamicMBean.java b/java/src/org/apache/log4j/jmx/AbstractDynamicMBean.java deleted file mode 100644 index a5a6574..0000000 --- a/java/src/org/apache/log4j/jmx/AbstractDynamicMBean.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.jmx; - -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Vector; - -import javax.management.Attribute; -import javax.management.AttributeList; -import javax.management.DynamicMBean; -import javax.management.InstanceAlreadyExistsException; -import javax.management.InstanceNotFoundException; -import javax.management.JMException; -import javax.management.MBeanRegistration; -import javax.management.MBeanRegistrationException; -import javax.management.MBeanServer; -import javax.management.NotCompliantMBeanException; -import javax.management.ObjectName; -import javax.management.RuntimeOperationsException; - -import org.apache.log4j.Logger; -import org.apache.log4j.Appender; - -public abstract class AbstractDynamicMBean implements DynamicMBean, - MBeanRegistration { - - String dClassName; - MBeanServer server; - private final Vector mbeanList = new Vector(); - - /** - * Get MBean name. - * @param appender appender, may not be null. - * @return name. - * @since 1.2.16 - */ - static protected String getAppenderName(final Appender appender){ - String name = appender.getName(); - if (name == null || name.trim().length() == 0) { - // try to get some form of a name, because null is not allowed (exception), and empty string certainly isn't useful in JMX.. - name = appender.toString(); - } - return name; - } - - - /** - * Enables the to get the values of several attributes of the Dynamic MBean. - */ - public - AttributeList getAttributes(String[] attributeNames) { - - // Check attributeNames is not null to avoid NullPointerException later on - if (attributeNames == null) { - throw new RuntimeOperationsException( - new IllegalArgumentException("attributeNames[] cannot be null"), - "Cannot invoke a getter of " + dClassName); - } - - AttributeList resultList = new AttributeList(); - - // if attributeNames is empty, return an empty result list - if (attributeNames.length == 0) - return resultList; - - // build the result attribute list - for (int i=0 ; i 0) { - val = attributeName.substring(0, k)+'='+ attributeName.substring(k+3); - } - try { - return new ObjectName("log4j:"+val); - } catch(JMException e) { - log.error("Could not create ObjectName" + val); - } catch(RuntimeException e) { - log.error("Could not create ObjectName" + val); - } - } - - - - // If attributeName has not been recognized throw an AttributeNotFoundException - throw(new AttributeNotFoundException("Cannot find " + attributeName + - " attribute in " + dClassName)); - - } - - - public - void addAppenderEvent(Category logger, Appender appender) { - log.debug("addAppenderEvent called: logger="+logger.getName()+ - ", appender="+appender.getName()); - Notification n = new Notification(ADD_APPENDER+logger.getName(), this, 0); - n.setUserData(appender); - log.debug("sending notification."); - nbs.sendNotification(n); - } - - public - void removeAppenderEvent(Category cat, Appender appender) { - log.debug("removeAppenderCalled: logger="+cat.getName()+ - ", appender="+appender.getName()); - } - - public - void postRegister(java.lang.Boolean registrationDone) { - log.debug("postRegister is called."); - hierarchy.addHierarchyEventListener(this); - Logger root = hierarchy.getRootLogger(); - addLoggerMBean(root); - } - - public - void removeNotificationListener(NotificationListener listener) - throws ListenerNotFoundException { - nbs.removeNotificationListener(listener); - } - - public - void setAttribute(Attribute attribute) throws AttributeNotFoundException, - InvalidAttributeValueException, - MBeanException, - ReflectionException { - - // Check attribute is not null to avoid NullPointerException later on - if (attribute == null) { - throw new RuntimeOperationsException( - new IllegalArgumentException("Attribute cannot be null"), - "Cannot invoke a setter of "+dClassName+" with null attribute"); - } - String name = attribute.getName(); - Object value = attribute.getValue(); - - if (name == null) { - throw new RuntimeOperationsException( - new IllegalArgumentException("Attribute name cannot be null"), - "Cannot invoke the setter of "+dClassName+ - " with null attribute name"); - } - - if(name.equals(THRESHOLD)) { - Level l = OptionConverter.toLevel((String) value, - hierarchy.getThreshold()); - hierarchy.setThreshold(l); - } - - - } -} diff --git a/java/src/org/apache/log4j/jmx/LayoutDynamicMBean.java b/java/src/org/apache/log4j/jmx/LayoutDynamicMBean.java deleted file mode 100644 index 4be6b63..0000000 --- a/java/src/org/apache/log4j/jmx/LayoutDynamicMBean.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.jmx; - -import java.lang.reflect.Constructor; -import org.apache.log4j.Logger; -import org.apache.log4j.Level; -import org.apache.log4j.Layout; -import org.apache.log4j.helpers.OptionConverter; -import org.apache.log4j.spi.OptionHandler; - -import java.util.Vector; -import java.util.Hashtable; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; -import javax.management.MBeanAttributeInfo; -import javax.management.MBeanConstructorInfo; -import javax.management.MBeanNotificationInfo; -import javax.management.MBeanInfo; -import javax.management.Attribute; - -import javax.management.MBeanException; -import javax.management.AttributeNotFoundException; -import javax.management.RuntimeOperationsException; -import javax.management.ReflectionException; -import javax.management.InvalidAttributeValueException; -import javax.management.MBeanOperationInfo; -import javax.management.MBeanParameterInfo; - -import java.beans.Introspector; -import java.beans.BeanInfo; -import java.beans.PropertyDescriptor; -import java.beans.IntrospectionException; -import java.io.InterruptedIOException; - -public class LayoutDynamicMBean extends AbstractDynamicMBean { - - private MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1]; - private Vector dAttributes = new Vector(); - private String dClassName = this.getClass().getName(); - - private Hashtable dynamicProps = new Hashtable(5); - private MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1]; - private String dDescription = - "This MBean acts as a management facade for log4j layouts."; - - // This category instance is for logging. - private static Logger cat = Logger.getLogger(LayoutDynamicMBean.class); - - // We wrap this layout instance. - private Layout layout; - - public LayoutDynamicMBean(Layout layout) throws IntrospectionException { - this.layout = layout; - buildDynamicMBeanInfo(); - } - - private - void buildDynamicMBeanInfo() throws IntrospectionException { - Constructor[] constructors = this.getClass().getConstructors(); - dConstructors[0] = new MBeanConstructorInfo( - "LayoutDynamicMBean(): Constructs a LayoutDynamicMBean instance", - constructors[0]); - - - BeanInfo bi = Introspector.getBeanInfo(layout.getClass()); - PropertyDescriptor[] pd = bi.getPropertyDescriptors(); - - int size = pd.length; - - for(int i = 0; i < size; i++) { - String name = pd[i].getName(); - Method readMethod = pd[i].getReadMethod(); - Method writeMethod = pd[i].getWriteMethod(); - if(readMethod != null) { - Class returnClass = readMethod.getReturnType(); - if(isSupportedType(returnClass)) { - String returnClassName; - if(returnClass.isAssignableFrom(Level.class)) { - returnClassName = "java.lang.String"; - } else { - returnClassName = returnClass.getName(); - } - - dAttributes.add(new MBeanAttributeInfo(name, - returnClassName, - "Dynamic", - true, - writeMethod != null, - false)); - dynamicProps.put(name, new MethodUnion(readMethod, writeMethod)); - } - } - } - - MBeanParameterInfo[] params = new MBeanParameterInfo[0]; - - dOperations[0] = new MBeanOperationInfo("activateOptions", - "activateOptions(): add an layout", - params, - "void", - MBeanOperationInfo.ACTION); - } - - private - boolean isSupportedType(Class clazz) { - if(clazz.isPrimitive()) { - return true; - } - - if(clazz == String.class) { - return true; - } - if(clazz.isAssignableFrom(Level.class)) { - return true; - } - - return false; - } - - - - public - MBeanInfo getMBeanInfo() { - cat.debug("getMBeanInfo called."); - - MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()]; - dAttributes.toArray(attribs); - - return new MBeanInfo(dClassName, - dDescription, - attribs, - dConstructors, - dOperations, - new MBeanNotificationInfo[0]); - } - - public - Object invoke(String operationName, Object params[], String signature[]) - throws MBeanException, - ReflectionException { - - if(operationName.equals("activateOptions") && - layout instanceof OptionHandler) { - OptionHandler oh = (OptionHandler) layout; - oh.activateOptions(); - return "Options activated."; - } - return null; - } - - protected - Logger getLogger() { - return cat; - } - - - public - Object getAttribute(String attributeName) throws AttributeNotFoundException, - MBeanException, - ReflectionException { - - // Check attributeName is not null to avoid NullPointerException later on - if (attributeName == null) { - throw new RuntimeOperationsException(new IllegalArgumentException( - "Attribute name cannot be null"), - "Cannot invoke a getter of " + dClassName + " with null attribute name"); - } - - - MethodUnion mu = (MethodUnion) dynamicProps.get(attributeName); - - cat.debug("----name="+attributeName+", mu="+mu); - - if(mu != null && mu.readMethod != null) { - try { - return mu.readMethod.invoke(layout, null); - } catch(InvocationTargetException e) { - if (e.getTargetException() instanceof InterruptedException - || e.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - return null; - } catch(IllegalAccessException e) { - return null; - } catch(RuntimeException e) { - return null; - } - } - - - - // If attributeName has not been recognized throw an AttributeNotFoundException - throw(new AttributeNotFoundException("Cannot find " + attributeName + - " attribute in " + dClassName)); - - } - - - public - void setAttribute(Attribute attribute) throws AttributeNotFoundException, - InvalidAttributeValueException, - MBeanException, - ReflectionException { - - // Check attribute is not null to avoid NullPointerException later on - if (attribute == null) { - throw new RuntimeOperationsException( - new IllegalArgumentException("Attribute cannot be null"), - "Cannot invoke a setter of " + dClassName + - " with null attribute"); - } - String name = attribute.getName(); - Object value = attribute.getValue(); - - if (name == null) { - throw new RuntimeOperationsException( - new IllegalArgumentException("Attribute name cannot be null"), - "Cannot invoke the setter of "+dClassName+ - " with null attribute name"); - } - - - - MethodUnion mu = (MethodUnion) dynamicProps.get(name); - - if(mu != null && mu.writeMethod != null) { - Object[] o = new Object[1]; - - Class[] params = mu.writeMethod.getParameterTypes(); - if(params[0] == org.apache.log4j.Priority.class) { - value = OptionConverter.toLevel((String) value, - (Level) getAttribute(name)); - } - o[0] = value; - - try { - mu.writeMethod.invoke(layout, o); - - } catch(InvocationTargetException e) { - if (e.getTargetException() instanceof InterruptedException - || e.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - cat.error("FIXME", e); - } catch(IllegalAccessException e) { - cat.error("FIXME", e); - } catch(RuntimeException e) { - cat.error("FIXME", e); - } - } else { - throw(new AttributeNotFoundException("Attribute " + name + - " not found in " + - this.getClass().getName())); - } - } -} - - diff --git a/java/src/org/apache/log4j/jmx/LoggerDynamicMBean.java b/java/src/org/apache/log4j/jmx/LoggerDynamicMBean.java deleted file mode 100644 index b226319..0000000 --- a/java/src/org/apache/log4j/jmx/LoggerDynamicMBean.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.jmx; - -import org.apache.log4j.Appender; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.helpers.OptionConverter; - -import javax.management.Attribute; -import javax.management.AttributeNotFoundException; -import javax.management.InvalidAttributeValueException; -import javax.management.JMException; -import javax.management.MBeanAttributeInfo; -import javax.management.MBeanConstructorInfo; -import javax.management.MBeanException; -import javax.management.MBeanInfo; -import javax.management.MBeanNotificationInfo; -import javax.management.MBeanOperationInfo; -import javax.management.MBeanParameterInfo; -import javax.management.MalformedObjectNameException; -import javax.management.Notification; -import javax.management.NotificationListener; -import javax.management.ObjectName; -import javax.management.ReflectionException; -import javax.management.RuntimeOperationsException; -import java.lang.reflect.Constructor; -import java.util.Enumeration; -import java.util.Vector; - -public class LoggerDynamicMBean extends AbstractDynamicMBean - implements NotificationListener { - - private MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1]; - private MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1]; - - private Vector dAttributes = new Vector(); - private String dClassName = this.getClass().getName(); - - private String dDescription = - "This MBean acts as a management facade for a org.apache.log4j.Logger instance."; - - // This Logger instance is for logging. - private static Logger cat = Logger.getLogger(LoggerDynamicMBean.class); - - // We wrap this Logger instance. - private Logger logger; - - public LoggerDynamicMBean(Logger logger) { - this.logger = logger; - buildDynamicMBeanInfo(); - } - - public - void handleNotification(Notification notification, Object handback) { - cat.debug("Received notification: "+notification.getType()); - registerAppenderMBean((Appender) notification.getUserData() ); - - - } - - private - void buildDynamicMBeanInfo() { - Constructor[] constructors = this.getClass().getConstructors(); - dConstructors[0] = new MBeanConstructorInfo( - "HierarchyDynamicMBean(): Constructs a HierarchyDynamicMBean instance", - constructors[0]); - - dAttributes.add(new MBeanAttributeInfo("name", - "java.lang.String", - "The name of this Logger.", - true, - false, - false)); - - dAttributes.add(new MBeanAttributeInfo("priority", - "java.lang.String", - "The priority of this logger.", - true, - true, - false)); - - - - - - MBeanParameterInfo[] params = new MBeanParameterInfo[2]; - params[0] = new MBeanParameterInfo("class name", "java.lang.String", - "add an appender to this logger"); - params[1] = new MBeanParameterInfo("appender name", "java.lang.String", - "name of the appender"); - - dOperations[0] = new MBeanOperationInfo("addAppender", - "addAppender(): add an appender", - params, - "void", - MBeanOperationInfo.ACTION); - } - - protected - Logger getLogger() { - return logger; - } - - - public - MBeanInfo getMBeanInfo() { - //cat.debug("getMBeanInfo called."); - - MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()]; - dAttributes.toArray(attribs); - - MBeanInfo mb = new MBeanInfo(dClassName, - dDescription, - attribs, - dConstructors, - dOperations, - new MBeanNotificationInfo[0]); - //cat.debug("getMBeanInfo exit."); - return mb; - } - - public - Object invoke(String operationName, Object params[], String signature[]) - throws MBeanException, - ReflectionException { - - if(operationName.equals("addAppender")) { - addAppender((String) params[0], (String) params[1]); - return "Hello world."; - } - - return null; - } - - - public - Object getAttribute(String attributeName) throws AttributeNotFoundException, - MBeanException, - ReflectionException { - - // Check attributeName is not null to avoid NullPointerException later on - if (attributeName == null) { - throw new RuntimeOperationsException(new IllegalArgumentException( - "Attribute name cannot be null"), - "Cannot invoke a getter of " + dClassName + " with null attribute name"); - } - - // Check for a recognized attributeName and call the corresponding getter - if (attributeName.equals("name")) { - return logger.getName(); - } else if(attributeName.equals("priority")) { - Level l = logger.getLevel(); - if(l == null) { - return null; - } else { - return l.toString(); - } - } else if(attributeName.startsWith("appender=")) { - try { - return new ObjectName("log4j:"+attributeName ); - } catch(MalformedObjectNameException e) { - cat.error("Could not create ObjectName" + attributeName); - } catch(RuntimeException e) { - cat.error("Could not create ObjectName" + attributeName); - } - } - - - // If attributeName has not been recognized throw an AttributeNotFoundException - throw(new AttributeNotFoundException("Cannot find " + attributeName + - " attribute in " + dClassName)); - - } - - - void addAppender(String appenderClass, String appenderName) { - cat.debug("addAppender called with "+appenderClass+", "+appenderName); - Appender appender = (Appender) - OptionConverter.instantiateByClassName(appenderClass, - org.apache.log4j.Appender.class, - null); - appender.setName(appenderName); - logger.addAppender(appender); - - //appenderMBeanRegistration(); - - } - - - public - void setAttribute(Attribute attribute) throws AttributeNotFoundException, - InvalidAttributeValueException, - MBeanException, - ReflectionException { - - // Check attribute is not null to avoid NullPointerException later on - if (attribute == null) { - throw new RuntimeOperationsException( - new IllegalArgumentException("Attribute cannot be null"), - "Cannot invoke a setter of " + dClassName + - " with null attribute"); - } - String name = attribute.getName(); - Object value = attribute.getValue(); - - if (name == null) { - throw new RuntimeOperationsException( - new IllegalArgumentException("Attribute name cannot be null"), - "Cannot invoke the setter of "+dClassName+ - " with null attribute name"); - } - - - if(name.equals("priority")) { - if (value instanceof String) { - String s = (String) value; - Level p = logger.getLevel(); - if(s.equalsIgnoreCase("NULL")) { - p = null; - } else { - p = OptionConverter.toLevel(s, p); - } - logger.setLevel(p); - } - } else { - throw(new AttributeNotFoundException("Attribute " + name + - " not found in " + - this.getClass().getName())); - } - } - - void appenderMBeanRegistration() { - Enumeration enumeration = logger.getAllAppenders(); - while(enumeration.hasMoreElements()) { - Appender appender = (Appender) enumeration.nextElement(); - registerAppenderMBean(appender); - } - } - - void registerAppenderMBean(Appender appender) { - String name = getAppenderName(appender); - cat.debug("Adding AppenderMBean for appender named "+name); - ObjectName objectName = null; - try { - AppenderDynamicMBean appenderMBean = new AppenderDynamicMBean(appender); - objectName = new ObjectName("log4j", "appender", name); - if (!server.isRegistered(objectName)) { - registerMBean(appenderMBean, objectName); - dAttributes.add(new MBeanAttributeInfo("appender=" + name, "javax.management.ObjectName", - "The " + name + " appender.", true, true, false)); - } - - } catch(JMException e) { - cat.error("Could not add appenderMBean for ["+name+"].", e); - } catch(java.beans.IntrospectionException e) { - cat.error("Could not add appenderMBean for ["+name+"].", e); - } catch(RuntimeException e) { - cat.error("Could not add appenderMBean for ["+name+"].", e); - } - } - - public - void postRegister(java.lang.Boolean registrationDone) { - appenderMBeanRegistration(); - } -} diff --git a/java/src/org/apache/log4j/jmx/MethodUnion.java b/java/src/org/apache/log4j/jmx/MethodUnion.java deleted file mode 100644 index e9f2fb2..0000000 --- a/java/src/org/apache/log4j/jmx/MethodUnion.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.jmx; - -import java.lang.reflect.Method; - -class MethodUnion { - - Method readMethod; - Method writeMethod; - - MethodUnion( Method readMethod, Method writeMethod) { - this.readMethod = readMethod; - this.writeMethod = writeMethod; - } - -} diff --git a/java/src/org/apache/log4j/jmx/package.html b/java/src/org/apache/log4j/jmx/package.html deleted file mode 100644 index 6d1583a..0000000 --- a/java/src/org/apache/log4j/jmx/package.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - This package lets you manage log4j settings using JMX. It is - unfortunately not of production quality. - - \ No newline at end of file diff --git a/java/src/org/apache/log4j/lf5/AppenderFinalizer.java b/java/src/org/apache/log4j/lf5/AppenderFinalizer.java deleted file mode 100644 index a2a7019..0000000 --- a/java/src/org/apache/log4j/lf5/AppenderFinalizer.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5; - -import org.apache.log4j.lf5.viewer.LogBrokerMonitor; - -/** - * AppenderFinalizer has a single method that will finalize - * resources associated with a LogBrokerMonitor in the event - * that the LF5Appender class is destroyed, and the class loader - * is garbage collected. - * - * @author Brent Sprecher - */ - -// Contributed by ThoughtWorks Inc. - -public class AppenderFinalizer { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - protected LogBrokerMonitor _defaultMonitor = null; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public AppenderFinalizer(LogBrokerMonitor defaultMonitor) { - _defaultMonitor = defaultMonitor; - } - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - /** - * @throws java.lang.Throwable - */ - protected void finalize() throws Throwable { - System.out.println("Disposing of the default LogBrokerMonitor instance"); - _defaultMonitor.dispose(); - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} \ No newline at end of file diff --git a/java/src/org/apache/log4j/lf5/DefaultLF5Configurator.java b/java/src/org/apache/log4j/lf5/DefaultLF5Configurator.java deleted file mode 100644 index 31d7f5b..0000000 --- a/java/src/org/apache/log4j/lf5/DefaultLF5Configurator.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.lf5; - -import org.apache.log4j.PropertyConfigurator; -import org.apache.log4j.spi.Configurator; -import org.apache.log4j.spi.LoggerRepository; - -import java.io.IOException; -import java.net.URL; - -/** - * The DefaultLF5Configurator provides a default - * configuration for the LF5Appender. - * - * Note: The preferred method for configuring a LF5Appender - * is to use the LF5Manager class. This class ensures - * that configuration does not occur multiple times, and improves system - * performance. Reconfiguring the monitor multiple times can result in - * unexpected behavior. - * - * @author Brent Sprecher - */ - -// Contributed by ThoughtWorks Inc. - -public class DefaultLF5Configurator implements Configurator { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - /** - * This class should never be instantiated! It implements the - * Configurator - * interface, but does not provide the same functionality as full - * configurator class. - */ - private DefaultLF5Configurator() { - - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - /** - * This method configures the LF5Appender using a - * default configuration file. The default configuration file is - * defaultconfig.properties. - * @throws java.io.IOException - */ - public static void configure() throws IOException { - String resource = - "/org/apache/log4j/lf5/config/defaultconfig.properties"; - URL configFileResource = - DefaultLF5Configurator.class.getResource(resource); - - if (configFileResource != null) { - PropertyConfigurator.configure(configFileResource); - } else { - throw new IOException("Error: Unable to open the resource" + - resource); - } - - } - - /** - * This is a dummy method that will throw an - * IllegalStateException if used. - */ - public void doConfigure(URL configURL, LoggerRepository repository) { - throw new IllegalStateException("This class should NOT be" + - " instantiated!"); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} \ No newline at end of file diff --git a/java/src/org/apache/log4j/lf5/LF5Appender.java b/java/src/org/apache/log4j/lf5/LF5Appender.java deleted file mode 100644 index c466f3a..0000000 --- a/java/src/org/apache/log4j/lf5/LF5Appender.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.lf5; - -import java.awt.Toolkit; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.lf5.viewer.LogBrokerMonitor; -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.spi.LoggingEvent; - -/** - * LF5Appender logs events to a swing based logging - * console. The swing console supports turning categories on and off, - * multiple detail level views, as well as full text searching and many - * other capabilties. - * - * @author Brent Sprecher - */ - -// Contributed by ThoughtWorks Inc. - -public class LF5Appender extends AppenderSkeleton { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - protected LogBrokerMonitor _logMonitor; - protected static LogBrokerMonitor _defaultLogMonitor; - protected static AppenderFinalizer _finalizer; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - /** - * Constructs a LF5Appender using the default instance of - * the LogBrokerMonitor. This constructor should always - * be preferred over the - * LF5Appender(LogBrokerMonitor monitor) - * constructor, unless you need to spawn additional log monitoring - * windows. - */ - public LF5Appender() { - this(getDefaultInstance()); - } - - /** - * Constructs a LF5Appender using an instance of - * a LogBrokerMonitor supplied by the user. This - * constructor should only be used when you need to spawn - * additional log monitoring windows. - * - * @param monitor An instance of a LogBrokerMonitor - * created by the user. - */ - public LF5Appender(LogBrokerMonitor monitor) { - - if (monitor != null) { - _logMonitor = monitor; - } - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Appends a LoggingEvent record to the - * LF5Appender. - * @param event The LoggingEvent - * to be appended. - */ - public void append(LoggingEvent event) { - // Retrieve the information from the log4j LoggingEvent. - String category = event.getLoggerName(); - String logMessage = event.getRenderedMessage(); - String nestedDiagnosticContext = event.getNDC(); - String threadDescription = event.getThreadName(); - String level = event.getLevel().toString(); - long time = event.timeStamp; - LocationInfo locationInfo = event.getLocationInformation(); - - // Add the logging event information to a LogRecord - Log4JLogRecord record = new Log4JLogRecord(); - - record.setCategory(category); - record.setMessage(logMessage); - record.setLocation(locationInfo.fullInfo); - record.setMillis(time); - record.setThreadDescription(threadDescription); - - if (nestedDiagnosticContext != null) { - record.setNDC(nestedDiagnosticContext); - } else { - record.setNDC(""); - } - - if (event.getThrowableInformation() != null) { - record.setThrownStackTrace(event.getThrowableInformation()); - } - - try { - record.setLevel(LogLevel.valueOf(level)); - } catch (LogLevelFormatException e) { - // If the priority level doesn't match one of the predefined - // log levels, then set the level to warning. - record.setLevel(LogLevel.WARN); - } - - if (_logMonitor != null) { - _logMonitor.addMessage(record); - } - } - - /** - * This method is an empty implementation of the close() method inherited - * from the org.apache.log4j.Appender interface. - */ - public void close() { - } - - /** - * Returns a value that indicates whether this appender requires a - * Layout. This method always returns false. - * No layout is required for the LF5Appender. - */ - public boolean requiresLayout() { - return false; - } - - /** - * This method is used to set the property that controls whether - * the LogBrokerMonitor is hidden or closed when a user - * exits - * the monitor. By default, the LogBrokerMonitor will hide - * itself when the log window is exited, and the swing thread will - * continue to running in the background. If this property is - * set to true, the LogBrokerMonitor will call System.exit(0) - * and will shut down swing thread and the virtual machine. - * - * @param callSystemExitOnClose A boolean value indicating whether - * to call System.exit(0) when closing the log window. - */ - public void setCallSystemExitOnClose(boolean callSystemExitOnClose) { - _logMonitor.setCallSystemExitOnClose(callSystemExitOnClose); - } - - /** - * The equals method compares two LF5Appenders and determines whether - * they are equal. Two Appenders will be considered equal - * if, and only if, they both contain references to the same - * LogBrokerMonitor. - * - * @param compareTo A boolean value indicating whether - * the two LF5Appenders are equal. - */ - public boolean equals(LF5Appender compareTo) { - // If both reference the same LogBrokerMonitor, they are equal. - return _logMonitor == compareTo.getLogBrokerMonitor(); - } - - public LogBrokerMonitor getLogBrokerMonitor() { - return _logMonitor; - } - - public static void main(String[] args) { - new LF5Appender(); - } - - public void setMaxNumberOfRecords(int maxNumberOfRecords) { - _defaultLogMonitor.setMaxNumberOfLogRecords(maxNumberOfRecords); - } - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - /** - * @return The default instance of the LogBrokerMonitor. - */ - protected static synchronized LogBrokerMonitor getDefaultInstance() { - if (_defaultLogMonitor == null) { - try { - _defaultLogMonitor = - new LogBrokerMonitor(LogLevel.getLog4JLevels()); - _finalizer = new AppenderFinalizer(_defaultLogMonitor); - - _defaultLogMonitor.setFrameSize(getDefaultMonitorWidth(), - getDefaultMonitorHeight()); - _defaultLogMonitor.setFontSize(12); - _defaultLogMonitor.show(); - - } catch (SecurityException e) { - _defaultLogMonitor = null; - } - } - - return _defaultLogMonitor; - } - - /** - * @return the screen width from Toolkit.getScreenSize() - * if possible, otherwise returns 800 - * @see java.awt.Toolkit - */ - protected static int getScreenWidth() { - try { - return Toolkit.getDefaultToolkit().getScreenSize().width; - } catch (Throwable t) { - return 800; - } - } - - /** - * @return the screen height from Toolkit.getScreenSize() - * if possible, otherwise returns 600 - * @see java.awt.Toolkit - */ - protected static int getScreenHeight() { - try { - return Toolkit.getDefaultToolkit().getScreenSize().height; - } catch (Throwable t) { - return 600; - } - } - - protected static int getDefaultMonitorWidth() { - return (3 * getScreenWidth()) / 4; - } - - protected static int getDefaultMonitorHeight() { - return (3 * getScreenHeight()) / 4; - } - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} diff --git a/java/src/org/apache/log4j/lf5/Log4JLogRecord.java b/java/src/org/apache/log4j/lf5/Log4JLogRecord.java deleted file mode 100644 index 4393eb5..0000000 --- a/java/src/org/apache/log4j/lf5/Log4JLogRecord.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.lf5; - -import org.apache.log4j.spi.ThrowableInformation; - -/** - * A Log4JLogRecord encapsulates - * the details of your log4j LoggingEvent in a format usable - * by the LogBrokerMonitor. - * - * @author Brent Sprecher - */ - -// Contributed by ThoughtWorks Inc. - -public class Log4JLogRecord extends LogRecord { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - /** - * Constructs an instance of a Log4JLogRecord. - */ - public Log4JLogRecord() { - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - /** - * Determines which Priority levels will - * be displayed in colored font when the LogMonitorAppender - * renders this log message. By default, messages will be colored - * red if they are of Priority ERROR or FATAL. - * - * @return true if the log level is ERROR or FATAL. - */ - public boolean isSevereLevel() { - boolean isSevere = false; - - if (LogLevel.ERROR.equals(getLevel()) || - LogLevel.FATAL.equals(getLevel())) { - isSevere = true; - } - - return isSevere; - } - - /** - * Set stack trace information associated with this Log4JLogRecord. - * When this method is called, the stack trace in a - * String-based format is made - * available via the getThrownStackTrace() method. - * - * @param throwableInfo An org.apache.log4j.spi.ThrowableInformation to - * associate with this Log4JLogRecord. - * @see #getThrownStackTrace() - */ - public void setThrownStackTrace(ThrowableInformation throwableInfo) { - String[] stackTraceArray = throwableInfo.getThrowableStrRep(); - - StringBuffer stackTrace = new StringBuffer(); - String nextLine; - - for (int i = 0; i < stackTraceArray.length; i++) { - nextLine = stackTraceArray[i] + "\n"; - stackTrace.append(nextLine); - } - - _thrownStackTrace = stackTrace.toString(); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - diff --git a/java/src/org/apache/log4j/lf5/LogLevel.java b/java/src/org/apache/log4j/lf5/LogLevel.java deleted file mode 100644 index 080ed68..0000000 --- a/java/src/org/apache/log4j/lf5/LogLevel.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5; - -import java.awt.Color; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * The LogLevel class defines a set of standard logging levels. - * - * The logging Level objects are ordered and are specified by ordered - * integers. Enabling logging at a given level also enables logging at all - * higher levels. - * - * @author Michael J. Sikorsky - * @author Robert Shaw - * @author Brent Sprecher - * @author Richard Hurst - * @author Brad Marlborough - */ - -// Contributed by ThoughtWorks Inc. - -public class LogLevel implements java.io.Serializable { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - // log4j log levels. - public final static LogLevel FATAL = new LogLevel("FATAL", 0); - public final static LogLevel ERROR = new LogLevel("ERROR", 1); - public final static LogLevel WARN = new LogLevel("WARN", 2); - public final static LogLevel INFO = new LogLevel("INFO", 3); - public final static LogLevel DEBUG = new LogLevel("DEBUG", 4); - - // jdk1.4 log levels NOTE: also includes INFO - public final static LogLevel SEVERE = new LogLevel("SEVERE", 1); - public final static LogLevel WARNING = new LogLevel("WARNING", 2); - public final static LogLevel CONFIG = new LogLevel("CONFIG", 4); - public final static LogLevel FINE = new LogLevel("FINE", 5); - public final static LogLevel FINER = new LogLevel("FINER", 6); - public final static LogLevel FINEST = new LogLevel("FINEST", 7); - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected String _label; - protected int _precedence; - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - private static LogLevel[] _log4JLevels; - private static LogLevel[] _jdk14Levels; - private static LogLevel[] _allDefaultLevels; - private static Map _logLevelMap; - private static Map _logLevelColorMap; - private static Map _registeredLogLevelMap = new HashMap(); - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - static { - _log4JLevels = new LogLevel[]{FATAL, ERROR, WARN, INFO, DEBUG}; - _jdk14Levels = new LogLevel[]{SEVERE, WARNING, INFO, - CONFIG, FINE, FINER, FINEST}; - _allDefaultLevels = new LogLevel[]{FATAL, ERROR, WARN, INFO, DEBUG, - SEVERE, WARNING, CONFIG, FINE, FINER, FINEST}; - - _logLevelMap = new HashMap(); - for (int i = 0; i < _allDefaultLevels.length; i++) { - _logLevelMap.put(_allDefaultLevels[i].getLabel(), _allDefaultLevels[i]); - } - - // prepopulate map with levels and text color of black - _logLevelColorMap = new HashMap(); - for (int i = 0; i < _allDefaultLevels.length; i++) { - _logLevelColorMap.put(_allDefaultLevels[i], Color.black); - } - } - - public LogLevel(String label, int precedence) { - _label = label; - _precedence = precedence; - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Return the Label of the LogLevel. - */ - public String getLabel() { - return _label; - } - - /** - * Returns true if the level supplied is encompassed by this level. - * For example, LogLevel.SEVERE encompasses no other LogLevels and - * LogLevel.FINE encompasses all other LogLevels. By definition, - * a LogLevel encompasses itself. - */ - public boolean encompasses(LogLevel level) { - if (level.getPrecedence() <= getPrecedence()) { - return true; - } - - return false; - } - - /** - * Convert a log level label into a LogLevel object. - * - * @param level The label of a level to be converted into a LogLevel. - * @return LogLevel The LogLevel with a label equal to level. - * @throws LogLevelFormatException Is thrown when the level can not be - * converted into a LogLevel. - */ - public static LogLevel valueOf(String level) - throws LogLevelFormatException { - LogLevel logLevel = null; - if (level != null) { - level = level.trim().toUpperCase(); - logLevel = (LogLevel) _logLevelMap.get(level); - } - - // Didn't match, Check for registered LogLevels - if (logLevel == null && _registeredLogLevelMap.size() > 0) { - logLevel = (LogLevel) _registeredLogLevelMap.get(level); - } - - if (logLevel == null) { - StringBuffer buf = new StringBuffer(); - buf.append("Error while trying to parse (" + level + ") into"); - buf.append(" a LogLevel."); - throw new LogLevelFormatException(buf.toString()); - } - return logLevel; - } - - /** - * Registers a used defined LogLevel. - * - * @param logLevel The log level to be registered. Cannot be a default LogLevel - * @return LogLevel The replaced log level. - */ - public static LogLevel register(LogLevel logLevel) { - if (logLevel == null) return null; - - // ensure that this is not a default log level - if (_logLevelMap.get(logLevel.getLabel()) == null) { - return (LogLevel) _registeredLogLevelMap.put(logLevel.getLabel(), logLevel); - } - - return null; - } - - public static void register(LogLevel[] logLevels) { - if (logLevels != null) { - for (int i = 0; i < logLevels.length; i++) { - register(logLevels[i]); - } - } - } - - public static void register(List logLevels) { - if (logLevels != null) { - Iterator it = logLevels.iterator(); - while (it.hasNext()) { - register((LogLevel) it.next()); - } - } - } - - public boolean equals(Object o) { - boolean equals = false; - - if (o instanceof LogLevel) { - if (this.getPrecedence() == - ((LogLevel) o).getPrecedence()) { - equals = true; - } - - } - - return equals; - } - - public int hashCode() { - return _label.hashCode(); - } - - public String toString() { - return _label; - } - - // set a text color for a specific log level - public void setLogLevelColorMap(LogLevel level, Color color) { - // remove the old entry - _logLevelColorMap.remove(level); - // add the new color entry - if (color == null) { - color = Color.black; - } - _logLevelColorMap.put(level, color); - } - - public static void resetLogLevelColorMap() { - // empty the map - _logLevelColorMap.clear(); - - // repopulate map and reset text color black - for (int i = 0; i < _allDefaultLevels.length; i++) { - _logLevelColorMap.put(_allDefaultLevels[i], Color.black); - } - } - - /** - * @return A List of LogLevel objects that map - * to log4j Priority objects. - */ - public static List getLog4JLevels() { - return Arrays.asList(_log4JLevels); - } - - public static List getJdk14Levels() { - return Arrays.asList(_jdk14Levels); - } - - public static List getAllDefaultLevels() { - return Arrays.asList(_allDefaultLevels); - } - - public static Map getLogLevelColorMap() { - return _logLevelColorMap; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected int getPrecedence() { - return _precedence; - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/LogLevelFormatException.java b/java/src/org/apache/log4j/lf5/LogLevelFormatException.java deleted file mode 100644 index 1109e23..0000000 --- a/java/src/org/apache/log4j/lf5/LogLevelFormatException.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5; - -/** - * Thrown to indicate that the client has attempted to convert a string - * to one the LogLevel types, but the string does not have the appropriate - * format. - * - * @author Michael J. Sikorsky< - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class LogLevelFormatException extends Exception { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public LogLevelFormatException(String message) { - super(message); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/LogRecord.java b/java/src/org/apache/log4j/lf5/LogRecord.java deleted file mode 100644 index 4f4097f..0000000 --- a/java/src/org/apache/log4j/lf5/LogRecord.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; - -/** - * LogRecord. A LogRecord encapsulates the details of your desired log - * request. - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public abstract class LogRecord implements java.io.Serializable { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected static long _seqCount = 0; - - protected LogLevel _level; - protected String _message; - protected long _sequenceNumber; - protected long _millis; - protected String _category; - protected String _thread; - protected String _thrownStackTrace; - protected Throwable _thrown; - protected String _ndc; - protected String _location; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public LogRecord() { - super(); - - _millis = System.currentTimeMillis(); - _category = "Debug"; - _message = ""; - _level = LogLevel.INFO; - _sequenceNumber = getNextId(); - _thread = Thread.currentThread().toString(); - _ndc = ""; - _location = ""; - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Get the level of this LogRecord. - * - * @return The LogLevel of this record. - * @see #setLevel(LogLevel) - * @see LogLevel - */ - public LogLevel getLevel() { - return (_level); - } - - /** - * Set the level of this LogRecord. - * - * @param level The LogLevel for this record. - * @see #getLevel() - * @see LogLevel - */ - public void setLevel(LogLevel level) { - _level = level; - } - - /** - * Abstract method. Must be overridden to indicate what log level - * to show in red. - */ - public abstract boolean isSevereLevel(); - - /** - * @return true if getThrown().toString() is a non-empty string. - */ - public boolean hasThrown() { - Throwable thrown = getThrown(); - if (thrown == null) { - return false; - } - String thrownString = thrown.toString(); - return thrownString != null && thrownString.trim().length() != 0; - } - - /** - * @return true if isSevereLevel() or hasThrown() returns true. - */ - public boolean isFatal() { - return isSevereLevel() || hasThrown(); - } - - /** - * Get the category asscociated with this LogRecord. For a more detailed - * description of what a category is see setCategory(). - * - * @return The category of this record. - * @see #setCategory(String) - */ - public String getCategory() { - return (_category); - } - - /** - * Set the category associated with this LogRecord. A category represents - * a hierarchical dot (".") separated namespace for messages. - * The definition of a category is application specific, but a common convention - * is as follows: - * - *

- * When logging messages - * for a particluar class you can use its class name: - * com.thoughtworks.framework.servlet.ServletServiceBroker.

- * Futhermore, to log a message for a particular method in a class - * add the method name: - * com.thoughtworks.framework.servlet.ServletServiceBroker.init(). - *

- * - * @param category The category for this record. - * @see #getCategory() - */ - public void setCategory(String category) { - _category = category; - } - - /** - * Get the message asscociated with this LogRecord. - * - * @return The message of this record. - * @see #setMessage(String) - */ - public String getMessage() { - return (_message); - } - - /** - * Set the message associated with this LogRecord. - * - * @param message The message for this record. - * @see #getMessage() - */ - public void setMessage(String message) { - _message = message; - } - - /** - * Get the sequence number associated with this LogRecord. Sequence numbers - * are generally assigned when a LogRecord is constructed. Sequence numbers - * start at 0 and increase with each newly constructed LogRocord. - * - * @return The sequence number of this record. - * @see #setSequenceNumber(long) - */ - public long getSequenceNumber() { - return (_sequenceNumber); - } - - /** - * Set the sequence number assocsiated with this LogRecord. A sequence number - * will automatically be assigned to evey newly constructed LogRecord, however, - * this method can override the value. - * - * @param number The sequence number. - * @see #getSequenceNumber() - */ - public void setSequenceNumber(long number) { - _sequenceNumber = number; - } - - /** - * Get the event time of this record in milliseconds from 1970. - * When a LogRecord is constructed the event time is set but may be - * overridden by calling setMillis(); - * - * @return The event time of this record in milliseconds from 1970. - * @see #setMillis(long) - */ - public long getMillis() { - return _millis; - } - - /** - * Set the event time of this record. When a LogRecord is constructed - * the event time is set but may be overridden by calling this method. - * - * @param millis The time in milliseconds from 1970. - * @see #getMillis() - */ - public void setMillis(long millis) { - _millis = millis; - } - - /** - * Get the thread description asscociated with this LogRecord. When a - * LogRecord is constructed, the thread description is set by calling: - * Thread.currentThread().toString(). You may supply a thread description - * of your own by calling the setThreadDescription(String) method. - * - * @return The thread description of this record. - * @see #setThreadDescription(String) - */ - public String getThreadDescription() { - return (_thread); - } - - /** - * Set the thread description associated with this LogRecord. When a - * LogRecord is constructed, the thread description is set by calling: - * Thread.currentThread().toString(). You may supply a thread description - * of your own by calling this method. - * - * @param threadDescription The description of the thread for this record. - * @see #getThreadDescription() - */ - public void setThreadDescription(String threadDescription) { - _thread = threadDescription; - } - - /** - * Get the stack trace in a String-based format for the associated Throwable - * of this LogRecord. The stack trace in a String-based format is set - * when the setThrown(Throwable) method is called. - * - *

- * Why do we need this method considering that we - * have the getThrown() and setThrown() methods? - * A Throwable object may not be serializable, however, a String representation - * of it is. Users of LogRecords should generally call this method over - * getThrown() for the reasons of serialization. - *

- * - * @return The Stack Trace for the asscoiated Throwable of this LogRecord. - * @see #setThrown(Throwable) - * @see #getThrown() - */ - public String getThrownStackTrace() { - return (_thrownStackTrace); - } - - /** - * Set the ThrownStackTrace for the log record. - * - * @param trace A String to associate with this LogRecord - * @see #getThrownStackTrace() - */ - public void setThrownStackTrace(String trace) { - _thrownStackTrace = trace; - } - - /** - * Get the Throwable associated with this LogRecord. - * - * @return The LogLevel of this record. - * @see #setThrown(Throwable) - * @see #getThrownStackTrace() - */ - public Throwable getThrown() { - return (_thrown); - } - - /** - * Set the Throwable associated with this LogRecord. When this method - * is called, the stack trace in a String-based format is made - * available via the getThrownStackTrace() method. - * - * @param thrown A Throwable to associate with this LogRecord. - * @see #getThrown() - * @see #getThrownStackTrace() - */ - public void setThrown(Throwable thrown) { - if (thrown == null) { - return; - } - _thrown = thrown; - StringWriter sw = new StringWriter(); - PrintWriter out = new PrintWriter(sw); - thrown.printStackTrace(out); - out.flush(); - _thrownStackTrace = sw.toString(); - try { - out.close(); - sw.close(); - } catch (IOException e) { - // Do nothing, this should not happen as it is StringWriter. - } - out = null; - sw = null; - } - - /** - * Return a String representation of this LogRecord. - */ - public String toString() { - StringBuffer buf = new StringBuffer(); - buf.append("LogRecord: [" + _level + ", " + _message + "]"); - return (buf.toString()); - } - - /** - * Get the NDC (nested diagnostic context) for this record. - * - * @return The string representing the NDC. - */ - public String getNDC() { - return _ndc; - } - - /** - * Set the NDC (nested diagnostic context) for this record. - * - * @param ndc A string representing the NDC. - */ - public void setNDC(String ndc) { - _ndc = ndc; - } - - /** - * Get the location in code where this LogRecord originated. - * - * @return The string containing the location information. - */ - public String getLocation() { - return _location; - } - - /** - * Set the location in code where this LogRecord originated. - * - * @param location A string containing location information. - */ - public void setLocation(String location) { - _location = location; - } - - /** - * Resets that sequence number to 0. - * - */ - public static synchronized void resetSequenceNumber() { - _seqCount = 0; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected static synchronized long getNextId() { - _seqCount++; - return _seqCount; - } - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - diff --git a/java/src/org/apache/log4j/lf5/LogRecordFilter.java b/java/src/org/apache/log4j/lf5/LogRecordFilter.java deleted file mode 100644 index 25a3e53..0000000 --- a/java/src/org/apache/log4j/lf5/LogRecordFilter.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5; - - -/** - * An interface for classes which filters LogRecords. Implementations - * represent a rule or condition which LogRecords may pass or fail. - * @see LogRecord - * - * @author Richard Wan - */ - -// Contributed by ThoughtWorks Inc. - -public interface LogRecordFilter { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * @return true if the specified LogRecord satisfies whatever condition - * implementing class tests for. - */ - public boolean passes(LogRecord record); - -} - diff --git a/java/src/org/apache/log4j/lf5/PassingLogRecordFilter.java b/java/src/org/apache/log4j/lf5/PassingLogRecordFilter.java deleted file mode 100644 index 178f6cb..0000000 --- a/java/src/org/apache/log4j/lf5/PassingLogRecordFilter.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5; - - -/** - * An implementation of LogRecordFilter which always returns true. - * - * @author Richard Wan - */ - -// Contributed by ThoughtWorks Inc. - -public class PassingLogRecordFilter implements LogRecordFilter { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * @return true; - */ - public boolean passes(LogRecord record) { - return true; - } - - /** - * Does nothing. - */ - public void reset() { - // do nothing - } - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} - diff --git a/java/src/org/apache/log4j/lf5/StartLogFactor5.java b/java/src/org/apache/log4j/lf5/StartLogFactor5.java deleted file mode 100644 index 914b02f..0000000 --- a/java/src/org/apache/log4j/lf5/StartLogFactor5.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5; - -import org.apache.log4j.lf5.viewer.LogBrokerMonitor; - -/** - * Starts an instance of the LogFactor5 console for off-line viewing. - * - * @author Brad Marlborough - * @author Richard Hurst - */ - -// Contributed by ThoughtWorks Inc. - -public class StartLogFactor5 { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * NetworkClient - starts a an instance of the LogFactor5 console and configures - * the console settings. - */ - public final static void main(String[] args) { - - LogBrokerMonitor monitor = new LogBrokerMonitor( - LogLevel.getLog4JLevels()); - - monitor.setFrameSize(LF5Appender.getDefaultMonitorWidth(), - LF5Appender.getDefaultMonitorHeight()); - monitor.setFontSize(12); - monitor.show(); - - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- - -} - - diff --git a/java/src/org/apache/log4j/lf5/util/AdapterLogRecord.java b/java/src/org/apache/log4j/lf5/util/AdapterLogRecord.java deleted file mode 100644 index 2e4ee6f..0000000 --- a/java/src/org/apache/log4j/lf5/util/AdapterLogRecord.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.util; - -import org.apache.log4j.lf5.LogLevel; -import org.apache.log4j.lf5.LogRecord; - -import java.io.PrintWriter; -import java.io.StringWriter; - -/** - *

A LogRecord to be used with the LogMonitorAdapter

- * - * @author Richard Hurst - */ - -// Contributed by ThoughtWorks Inc. - -public class AdapterLogRecord extends LogRecord { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - private static LogLevel severeLevel = null; - - private static StringWriter sw = new StringWriter(); - private static PrintWriter pw = new PrintWriter(sw); - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - public AdapterLogRecord() { - super(); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - public void setCategory(String category) { - super.setCategory(category); - super.setLocation(getLocationInfo(category)); - } - - public boolean isSevereLevel() { - if (severeLevel == null) return false; - return severeLevel.equals(getLevel()); - } - - public static void setSevereLevel(LogLevel level) { - severeLevel = level; - } - - public static LogLevel getSevereLevel() { - return severeLevel; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - protected String getLocationInfo(String category) { - String stackTrace = stackTraceToString(new Throwable()); - String line = parseLine(stackTrace, category); - return line; - } - - protected String stackTraceToString(Throwable t) { - String s = null; - - synchronized (sw) { - t.printStackTrace(pw); - s = sw.toString(); - sw.getBuffer().setLength(0); - } - - return s; - } - - protected String parseLine(String trace, String category) { - int index = trace.indexOf(category); - if (index == -1) return null; - trace = trace.substring(index); - trace = trace.substring(0, trace.indexOf(")") + 1); - return trace; - } - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} - diff --git a/java/src/org/apache/log4j/lf5/util/DateFormatManager.java b/java/src/org/apache/log4j/lf5/util/DateFormatManager.java deleted file mode 100644 index 6bed9cc..0000000 --- a/java/src/org/apache/log4j/lf5/util/DateFormatManager.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.util; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - -/** - * Date format manager. - * Utility class to help manage consistent date formatting and parsing. - * It may be advantageous to have multiple DateFormatManagers per - * application. For example, one for handling the output (formatting) of - * dates, and another one for handling the input (parsing) of dates. - * - * @author Robert Shaw - * @author Michael J. Sikorsky - */ - -// Contributed by ThoughtWorks Inc. -public class DateFormatManager { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - private TimeZone _timeZone = null; - private Locale _locale = null; - - private String _pattern = null; - private DateFormat _dateFormat = null; - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - public DateFormatManager() { - super(); - configure(); - } - - public DateFormatManager(TimeZone timeZone) { - super(); - - _timeZone = timeZone; - configure(); - } - - public DateFormatManager(Locale locale) { - super(); - - _locale = locale; - configure(); - } - - public DateFormatManager(String pattern) { - super(); - - _pattern = pattern; - configure(); - } - - public DateFormatManager(TimeZone timeZone, Locale locale) { - super(); - - _timeZone = timeZone; - _locale = locale; - configure(); - } - - public DateFormatManager(TimeZone timeZone, String pattern) { - super(); - - _timeZone = timeZone; - _pattern = pattern; - configure(); - } - - public DateFormatManager(Locale locale, String pattern) { - super(); - - _locale = locale; - _pattern = pattern; - configure(); - } - - public DateFormatManager(TimeZone timeZone, Locale locale, String pattern) { - super(); - - _timeZone = timeZone; - _locale = locale; - _pattern = pattern; - configure(); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public synchronized TimeZone getTimeZone() { - if (_timeZone == null) { - return TimeZone.getDefault(); - } else { - return _timeZone; - } - } - - public synchronized void setTimeZone(TimeZone timeZone) { - _timeZone = timeZone; - configure(); - } - - public synchronized Locale getLocale() { - if (_locale == null) { - return Locale.getDefault(); - } else { - return _locale; - } - } - - public synchronized void setLocale(Locale locale) { - _locale = locale; - configure(); - } - - public synchronized String getPattern() { - return _pattern; - } - - /** - * Set the pattern. i.e. "EEEEE, MMMMM d, yyyy hh:mm aaa" - */ - public synchronized void setPattern(String pattern) { - _pattern = pattern; - configure(); - } - - - /** - * This method has been deprecated in favour of getPattern(). - * @deprecated Use getPattern(). - */ - public synchronized String getOutputFormat() { - return _pattern; - } - - /** - * This method has been deprecated in favour of setPattern(). - * @deprecated Use setPattern(). - */ - public synchronized void setOutputFormat(String pattern) { - _pattern = pattern; - configure(); - } - - public synchronized DateFormat getDateFormatInstance() { - return _dateFormat; - } - - public synchronized void setDateFormatInstance(DateFormat dateFormat) { - _dateFormat = dateFormat; - // No reconfiguration necessary! - } - - public String format(Date date) { - return getDateFormatInstance().format(date); - } - - public String format(Date date, String pattern) { - DateFormat formatter = null; - formatter = getDateFormatInstance(); - if (formatter instanceof SimpleDateFormat) { - formatter = (SimpleDateFormat) (formatter.clone()); - ((SimpleDateFormat) formatter).applyPattern(pattern); - } - return formatter.format(date); - } - - /** - * @throws java.text.ParseException - */ - public Date parse(String date) throws ParseException { - return getDateFormatInstance().parse(date); - } - - /** - * @throws java.text.ParseException - */ - public Date parse(String date, String pattern) throws ParseException { - DateFormat formatter = null; - formatter = getDateFormatInstance(); - if (formatter instanceof SimpleDateFormat) { - formatter = (SimpleDateFormat) (formatter.clone()); - ((SimpleDateFormat) formatter).applyPattern(pattern); - } - return formatter.parse(date); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - private synchronized void configure() { - _dateFormat = SimpleDateFormat.getDateTimeInstance(DateFormat.FULL, - DateFormat.FULL, - getLocale()); - _dateFormat.setTimeZone(getTimeZone()); - - if (_pattern != null) { - ((SimpleDateFormat) _dateFormat).applyPattern(_pattern); - } - } - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} diff --git a/java/src/org/apache/log4j/lf5/util/LogFileParser.java b/java/src/org/apache/log4j/lf5/util/LogFileParser.java deleted file mode 100644 index ee4592d..0000000 --- a/java/src/org/apache/log4j/lf5/util/LogFileParser.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.util; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; - -import javax.swing.SwingUtilities; - -import org.apache.log4j.lf5.Log4JLogRecord; -import org.apache.log4j.lf5.LogLevel; -import org.apache.log4j.lf5.LogLevelFormatException; -import org.apache.log4j.lf5.LogRecord; -import org.apache.log4j.lf5.viewer.LogBrokerMonitor; -import org.apache.log4j.lf5.viewer.LogFactor5ErrorDialog; -import org.apache.log4j.lf5.viewer.LogFactor5LoadingDialog; - -/** - * Provides utility methods for input and output streams. - * - * @author Brad Marlborough - * @author Richard Hurst - */ - -// Contributed by ThoughtWorks Inc. - -public class LogFileParser implements Runnable { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - public static final String RECORD_DELIMITER = "[slf5s.start]"; - public static final String ATTRIBUTE_DELIMITER = "[slf5s."; - public static final String DATE_DELIMITER = ATTRIBUTE_DELIMITER + "DATE]"; - public static final String THREAD_DELIMITER = ATTRIBUTE_DELIMITER + "THREAD]"; - public static final String CATEGORY_DELIMITER = ATTRIBUTE_DELIMITER + "CATEGORY]"; - public static final String LOCATION_DELIMITER = ATTRIBUTE_DELIMITER + "LOCATION]"; - public static final String MESSAGE_DELIMITER = ATTRIBUTE_DELIMITER + "MESSAGE]"; - public static final String PRIORITY_DELIMITER = ATTRIBUTE_DELIMITER + "PRIORITY]"; - public static final String NDC_DELIMITER = ATTRIBUTE_DELIMITER + "NDC]"; - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - private static SimpleDateFormat _sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss,S"); - private LogBrokerMonitor _monitor; - LogFactor5LoadingDialog _loadDialog; - private InputStream _in = null; - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - public LogFileParser(File file) throws IOException, - FileNotFoundException { - this(new FileInputStream(file)); - } - - public LogFileParser(InputStream stream) throws IOException { - _in = stream; - } - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Starts a new thread to parse the log file and create a LogRecord. - * See running(). - * @param monitor LogBrokerMonitor - */ - public void parse(LogBrokerMonitor monitor) throws RuntimeException { - _monitor = monitor; - Thread t = new Thread(this); - t.start(); - } - - /** - * Parses the file and creates new log records and adds the record - * to the monitor. - */ - public void run() { - - int index = 0; - int counter = 0; - LogRecord temp; - boolean isLogFile = false; - - _loadDialog = new LogFactor5LoadingDialog( - _monitor.getBaseFrame(), "Loading file..."); - - - try { - String logRecords = loadLogFile(_in); - - while ((counter = logRecords.indexOf(RECORD_DELIMITER, index)) != -1) { - temp = createLogRecord(logRecords.substring(index, counter)); - isLogFile = true; - - if (temp != null) { - _monitor.addMessage(temp); - } - - index = counter + RECORD_DELIMITER.length(); - } - - if (index < logRecords.length() && isLogFile) { - temp = createLogRecord(logRecords.substring(index)); - - if (temp != null) { - _monitor.addMessage(temp); - } - } - - if (isLogFile == false) { - throw new RuntimeException("Invalid log file format"); - } - SwingUtilities.invokeLater(new Runnable() { - public void run() { - destroyDialog(); - } - }); - - } catch (RuntimeException e) { - destroyDialog(); - displayError("Error - Invalid log file format.\nPlease see documentation" - + " on how to load log files."); - } catch (IOException e) { - destroyDialog(); - displayError("Error - Unable to load log file!"); - } - - _in = null; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - protected void displayError(String message) { - LogFactor5ErrorDialog error = new LogFactor5ErrorDialog( - _monitor.getBaseFrame(), message); - - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - private void destroyDialog() { - _loadDialog.hide(); - _loadDialog.dispose(); - } - - /** - * Loads a log file from a web server into the LogFactor5 GUI. - */ - private String loadLogFile(InputStream stream) throws IOException { - BufferedInputStream br = new BufferedInputStream(stream); - - int count = 0; - int size = br.available(); - - StringBuffer sb = null; - if (size > 0) { - sb = new StringBuffer(size); - } else { - sb = new StringBuffer(1024); - } - - while ((count = br.read()) != -1) { - sb.append((char) count); - } - - br.close(); - br = null; - return sb.toString(); - - } - - private String parseAttribute(String name, String record) { - - int index = record.indexOf(name); - - if (index == -1) { - return null; - } - - return getAttribute(index, record); - } - - private long parseDate(String record) { - try { - String s = parseAttribute(DATE_DELIMITER, record); - - if (s == null) { - return 0; - } - - Date d = _sdf.parse(s); - - return d.getTime(); - } catch (ParseException e) { - return 0; - } - } - - private LogLevel parsePriority(String record) { - String temp = parseAttribute(PRIORITY_DELIMITER, record); - - if (temp != null) { - try { - return LogLevel.valueOf(temp); - } catch (LogLevelFormatException e) { - return LogLevel.DEBUG; - } - - } - - return LogLevel.DEBUG; - } - - private String parseThread(String record) { - return parseAttribute(THREAD_DELIMITER, record); - } - - private String parseCategory(String record) { - return parseAttribute(CATEGORY_DELIMITER, record); - } - - private String parseLocation(String record) { - return parseAttribute(LOCATION_DELIMITER, record); - } - - private String parseMessage(String record) { - return parseAttribute(MESSAGE_DELIMITER, record); - } - - private String parseNDC(String record) { - return parseAttribute(NDC_DELIMITER, record); - } - - private String parseThrowable(String record) { - return getAttribute(record.length(), record); - } - - private LogRecord createLogRecord(String record) { - if (record == null || record.trim().length() == 0) { - return null; - } - - LogRecord lr = new Log4JLogRecord(); - lr.setMillis(parseDate(record)); - lr.setLevel(parsePriority(record)); - lr.setCategory(parseCategory(record)); - lr.setLocation(parseLocation(record)); - lr.setThreadDescription(parseThread(record)); - lr.setNDC(parseNDC(record)); - lr.setMessage(parseMessage(record)); - lr.setThrownStackTrace(parseThrowable(record)); - - return lr; - } - - - private String getAttribute(int index, String record) { - int start = record.lastIndexOf(ATTRIBUTE_DELIMITER, index - 1); - - if (start == -1) { - return record.substring(0, index); - } - - start = record.indexOf("]", start); - - return record.substring(start + 1, index).trim(); - } - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- - -} diff --git a/java/src/org/apache/log4j/lf5/util/LogMonitorAdapter.java b/java/src/org/apache/log4j/lf5/util/LogMonitorAdapter.java deleted file mode 100644 index 6ba927c..0000000 --- a/java/src/org/apache/log4j/lf5/util/LogMonitorAdapter.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.util; - -import java.awt.Toolkit; -import java.util.Arrays; -import java.util.List; - -import org.apache.log4j.lf5.LogLevel; -import org.apache.log4j.lf5.LogRecord; -import org.apache.log4j.lf5.viewer.LogBrokerMonitor; - -/** - *

LogMonitorAdapter facilitates the usage of the LogMonitor

- * - * @author Richard Hurst - */ - -// Contributed by ThoughtWorks Inc. - -public class LogMonitorAdapter { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - public static final int LOG4J_LOG_LEVELS = 0; - public static final int JDK14_LOG_LEVELS = 1; - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - private LogBrokerMonitor _logMonitor; - private LogLevel _defaultLevel = null; - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - private LogMonitorAdapter(List userDefinedLevels) { - super(); - // set the default level to be the first entry in the list - _defaultLevel = (LogLevel) userDefinedLevels.get(0); - _logMonitor = new LogBrokerMonitor(userDefinedLevels); - - _logMonitor.setFrameSize(getDefaultMonitorWidth(), - getDefaultMonitorHeight()); - _logMonitor.setFontSize(12); - _logMonitor.show(); - } - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - /** - *

Creates an instance of LogMonitorAdapter using the - * log levels inticated by the parameter. Log4J and JDK1.4 both have default - * LogLevels which are set but these levels can be overriden.

- * - * @param loglevels An integer representing either Log4J or JDK1.4 logging levels - * @return LogMonitorAdapter - */ - public static LogMonitorAdapter newInstance(int loglevels) { - LogMonitorAdapter adapter; - if (loglevels == JDK14_LOG_LEVELS) { - adapter = newInstance(LogLevel.getJdk14Levels()); - adapter.setDefaultLevel(LogLevel.FINEST); - adapter.setSevereLevel(LogLevel.SEVERE); - } else { - adapter = newInstance(LogLevel.getLog4JLevels()); - adapter.setDefaultLevel(LogLevel.DEBUG); - adapter.setSevereLevel(LogLevel.FATAL); - } - return adapter; - } - - /** - *

Creates an instance of LogMonitorAdapter using the specified LogLevels. - * The first LogLevel in the array is used as the default LogLevel unless - * changed using the setDefaultLevel method.

- * - * @param userDefined An array of user defined LogLevel objects. - * @return LogMonitorAdapter - */ - public static LogMonitorAdapter newInstance(LogLevel[] userDefined) { - if (userDefined == null) { - return null; - } - return newInstance(Arrays.asList(userDefined)); - } - - /** - *

Creates an instance of LogMonitorAdapter using the specified LogLevels. - * The first LogLevel in the List is used as the default LogLevel unless - * changed using the setDefaultLevel method.

- * - * @param userDefinedLevels A list of user defined LogLevel objects. - * @return LogMonitorAdapter - */ - public static LogMonitorAdapter newInstance(List userDefinedLevels) { - return new LogMonitorAdapter(userDefinedLevels); - } - - /** - *

Adds a LogRecord to the LogMonitor.

- * - * @param record The LogRecord object to be logged in the logging monitor. - */ - public void addMessage(LogRecord record) { - _logMonitor.addMessage(record); - } - - /** - *

Set the maximum number of records to be displayed in the monitor

- * - * @param maxNumberOfRecords - */ - public void setMaxNumberOfRecords(int maxNumberOfRecords) { - _logMonitor.setMaxNumberOfLogRecords(maxNumberOfRecords); - } - - /** - *

Set the default log level to be used when logging messages without - * specifying a LogLevel.

- * - * @param level - */ - public void setDefaultLevel(LogLevel level) { - _defaultLevel = level; - } - - /** - *

Gets the default LogLevel for the Adapter.

- * - * @return LogLevel - */ - public LogLevel getDefaultLevel() { - return _defaultLevel; - } - - /** - *

Sets the Severe LogLevel.

- * - * @param level - */ - public void setSevereLevel(LogLevel level) { - AdapterLogRecord.setSevereLevel(level); - } - - /** - *

Gets the current Severe LogLevel

- * - * @return LogLevel - */ - public LogLevel getSevereLevel() { - return AdapterLogRecord.getSevereLevel(); - } - - /** - *

Log a complete message to the Monitor.

- * - * @param category The category to be used - * @param level The log level to apply to the message - * @param message The message - * @param t The throwable content of the message - * @param NDC The NDC really only applies to Log4J and the parameter can - * usually be ignored. - */ - public void log(String category, LogLevel level, String message, - Throwable t, String NDC) { - AdapterLogRecord record = new AdapterLogRecord(); - record.setCategory(category); - record.setMessage(message); - record.setNDC(NDC); - record.setThrown(t); - - if (level == null) { - record.setLevel(getDefaultLevel()); - } else { - record.setLevel(level); - } - - addMessage(record); - } - - /** - *

Log a message to the Monitor and use the default LogLevel.

- * - * @param category The category to be used - * @param message The message - */ - public void log(String category, String message) { - log(category, null, message); - } - - /** - *

Log a message to the Monitor.

- * - * @param category The category to be used - * @param level The log level to apply to the message - * @param message The message - * @param NDC - */ - public void log(String category, LogLevel level, String message, String NDC) { - log(category, level, message, null, NDC); - } - - /** - *

Log a message to the Monitor.

- * - * @param category The category to be used - * @param level The log level to apply to the message - * @param message The message - * @param t The throwable content of the message - */ - public void log(String category, LogLevel level, String message, - Throwable t) { - log(category, level, message, t, null); - } - - /** - *

Log a message to the Monitor.

- * - * @param category The category to be used - * @param level The log level to apply to the message - * @param message The message - */ - public void log(String category, LogLevel level, String message) { - log(category, level, message, null, null); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - /** - * @return the screen width from Toolkit.getScreenSize() - * if possible, otherwise returns 800 - * @see java.awt.Toolkit - */ - protected static int getScreenWidth() { - try { - return Toolkit.getDefaultToolkit().getScreenSize().width; - } catch (Throwable t) { - return 800; - } - } - - /** - * @return the screen height from Toolkit.getScreenSize() - * if possible, otherwise returns 600 - * @see java.awt.Toolkit - */ - protected static int getScreenHeight() { - try { - return Toolkit.getDefaultToolkit().getScreenSize().height; - } catch (Throwable t) { - return 600; - } - } - - protected static int getDefaultMonitorWidth() { - return (3 * getScreenWidth()) / 4; - } - - protected static int getDefaultMonitorHeight() { - return (3 * getScreenHeight()) / 4; - } - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} - diff --git a/java/src/org/apache/log4j/lf5/util/Resource.java b/java/src/org/apache/log4j/lf5/util/Resource.java deleted file mode 100644 index 66293cf..0000000 --- a/java/src/org/apache/log4j/lf5/util/Resource.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.util; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; - -/** - * Resource encapsulates access to Resources via the Classloader. - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class Resource { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected String _name; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - /** - * Default, no argument constructor. - */ - public Resource() { - super(); - } - - /** - * Construct a Resource given a name. - * - * @see #setName(String) - */ - public Resource(String name) { - _name = name; - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Set the name of the resource. - *

- * A resource is some data (images, audio, text, etc) that can be accessed - * by class code in a way that is independent of the location of the code. - *

- *

- * The name of a resource is a "/"-separated path name that identifies - * the resource. - *

- * - * @see #getName() - */ - public void setName(String name) { - _name = name; - } - - /** - * Get the name of the resource. Set setName() for a description of - * a resource. - * - * @see #setName - */ - public String getName() { - return (_name); - } - - /** - * Get the InputStream for this Resource. Uses the classloader - * from this Resource. - * - * @see #getInputStreamReader - * @see ResourceUtils - */ - public InputStream getInputStream() { - InputStream in = ResourceUtils.getResourceAsStream(this, this); - - return (in); - } - - /** - * Get the InputStreamReader for this Resource. Uses the classloader from - * this Resource. - * - * @see #getInputStream - * @see ResourceUtils - */ - public InputStreamReader getInputStreamReader() { - InputStream in = ResourceUtils.getResourceAsStream(this, this); - - if (in == null) { - return null; - } - - InputStreamReader reader = new InputStreamReader(in); - - return reader; - } - - /** - * Get the URL of the Resource. Uses the classloader from this Resource. - * - * @see ResourceUtils - */ - public URL getURL() { - return (ResourceUtils.getResourceAsURL(this, this)); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/util/ResourceUtils.java b/java/src/org/apache/log4j/lf5/util/ResourceUtils.java deleted file mode 100644 index 5f022b6..0000000 --- a/java/src/org/apache/log4j/lf5/util/ResourceUtils.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.util; - -import java.io.InputStream; -import java.net.URL; - -/** - * ResourceUtils. Provide a set of convenience methods for working with - * Resources. - * - * @see org.apache.log4j.lf5.util.Resource - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class ResourceUtils { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Get the InputStream for this resource. Note: to convert an InputStream - * into an InputReader, use: new InputStreamReader(InputStream). - * - * @param object The object to grab the Classloader from. - * This parameter is quite important from a - * visibility of resources standpoint as the - * hierarchy of Classloaders plays a role. - * - * @param resource The resource to load. - * - * @return If the Resource was found, the InputStream, otherwise null. - * - * @see Resource - * @see #getResourceAsURL(Object,Resource) - * @see InputStream - */ - public static InputStream getResourceAsStream(Object object, Resource resource) { - ClassLoader loader = object.getClass().getClassLoader(); - - InputStream in = null; - - if (loader != null) { - in = loader.getResourceAsStream(resource.getName()); - } else { - in = ClassLoader.getSystemResourceAsStream(resource.getName()); - } - - return in; - } - - /** - * Get the URL for this resource. - * - * @param object The object to grab the Classloader from. - * This parameter is quite important from a - * visibility of resources standpoint as the - * hierarchy of Classloaders plays a role. - * - * @param resource The resource to load. - * - * @return If the Resource was found, the URL, otherwise null. - * - * @see Resource - * @see #getResourceAsStream(Object,Resource) - */ - public static URL getResourceAsURL(Object object, Resource resource) { - ClassLoader loader = object.getClass().getClassLoader(); - - URL url = null; - - if (loader != null) { - url = loader.getResource(resource.getName()); - } else { - url = ClassLoader.getSystemResource(resource.getName()); - } - - return (url); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/util/StreamUtils.java b/java/src/org/apache/log4j/lf5/util/StreamUtils.java deleted file mode 100644 index 183f9aa..0000000 --- a/java/src/org/apache/log4j/lf5/util/StreamUtils.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.lf5.util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Provides utility methods for input and output streams. - * - * @author Richard Wan - */ - -// Contributed by ThoughtWorks Inc. - -public abstract class StreamUtils { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - /** - * Default value is 2048. - */ - public static final int DEFAULT_BUFFER_SIZE = 2048; - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Copies information from the input stream to the output stream using - * a default buffer size of 2048 bytes. - * @throws java.io.IOException - */ - public static void copy(InputStream input, OutputStream output) - throws IOException { - copy(input, output, DEFAULT_BUFFER_SIZE); - } - - /** - * Copies information from the input stream to the output stream using - * the specified buffer size - * @throws java.io.IOException - */ - public static void copy(InputStream input, - OutputStream output, - int bufferSize) - throws IOException { - byte[] buf = new byte[bufferSize]; - int bytesRead = input.read(buf); - while (bytesRead != -1) { - output.write(buf, 0, bytesRead); - bytesRead = input.read(buf); - } - output.flush(); - } - - /** - * Copies information between specified streams and then closes - * both of the streams. - * @throws java.io.IOException - */ - public static void copyThenClose(InputStream input, OutputStream output) - throws IOException { - copy(input, output); - input.close(); - output.close(); - } - - /** - * @return a byte[] containing the information contained in the - * specified InputStream. - * @throws java.io.IOException - */ - public static byte[] getBytes(InputStream input) - throws IOException { - ByteArrayOutputStream result = new ByteArrayOutputStream(); - copy(input, result); - result.close(); - return result.toByteArray(); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- - -} diff --git a/java/src/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java b/java/src/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java deleted file mode 100644 index 2c68180..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import org.apache.log4j.lf5.LogRecord; -import org.apache.log4j.lf5.LogRecordFilter; -import org.apache.log4j.lf5.PassingLogRecordFilter; - -import javax.swing.table.AbstractTableModel; -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.List; - - -/** - * A TableModel for LogRecords which includes filtering support. - * - * @author Richard Wan - * @author Brent Sprecher - */ - -// Contributed by ThoughtWorks Inc. - -public class FilteredLogTableModel - extends AbstractTableModel { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - protected LogRecordFilter _filter = new PassingLogRecordFilter(); - protected List _allRecords = new ArrayList(); - protected List _filteredRecords; - protected int _maxNumberOfLogRecords = 5000; - protected String[] _colNames = {"Date", - "Thread", - "Message #", - "Level", - "NDC", - "Category", - "Message", - "Location", - "Thrown"}; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public FilteredLogTableModel() { - super(); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public void setLogRecordFilter(LogRecordFilter filter) { - _filter = filter; - } - - public LogRecordFilter getLogRecordFilter() { - return _filter; - } - - public String getColumnName(int i) { - return _colNames[i]; - } - - public int getColumnCount() { - return _colNames.length; - } - - public int getRowCount() { - return getFilteredRecords().size(); - } - - public int getTotalRowCount() { - return _allRecords.size(); - } - - public Object getValueAt(int row, int col) { - LogRecord record = getFilteredRecord(row); - return getColumn(col, record); - } - - public void setMaxNumberOfLogRecords(int maxNumRecords) { - if (maxNumRecords > 0) { - _maxNumberOfLogRecords = maxNumRecords; - } - - } - - public synchronized boolean addLogRecord(LogRecord record) { - - _allRecords.add(record); - - if (_filter.passes(record) == false) { - return false; - } - getFilteredRecords().add(record); - fireTableRowsInserted(getRowCount(), getRowCount()); - trimRecords(); - return true; - } - - /** - * Forces the LogTableModel to requery its filters to determine - * which records to display. - */ - public synchronized void refresh() { - _filteredRecords = createFilteredRecordsList(); - fireTableDataChanged(); - } - - public synchronized void fastRefresh() { - _filteredRecords.remove(0); - fireTableRowsDeleted(0, 0); - } - - - /** - * Clears all records from the LogTableModel - */ - public synchronized void clear() { - _allRecords.clear(); - _filteredRecords.clear(); - fireTableDataChanged(); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected List getFilteredRecords() { - if (_filteredRecords == null) { - refresh(); - } - return _filteredRecords; - } - - protected List createFilteredRecordsList() { - List result = new ArrayList(); - Iterator records = _allRecords.iterator(); - LogRecord current; - while (records.hasNext()) { - current = (LogRecord) records.next(); - if (_filter.passes(current)) { - result.add(current); - } - } - return result; - } - - protected LogRecord getFilteredRecord(int row) { - List records = getFilteredRecords(); - int size = records.size(); - if (row < size) { - return (LogRecord) records.get(row); - } - // a minor problem has happened. JTable has asked for - // a row outside the bounds, because the size of - // _filteredRecords has changed while it was looping. - // return the last row. - return (LogRecord) records.get(size - 1); - - } - - protected Object getColumn(int col, LogRecord lr) { - if (lr == null) { - return "NULL Column"; - } - String date = new Date(lr.getMillis()).toString(); - switch (col) { - case 0: - return date + " (" + lr.getMillis() + ")"; - case 1: - return lr.getThreadDescription(); - case 2: - return new Long(lr.getSequenceNumber()); - case 3: - return lr.getLevel(); - case 4: - return lr.getNDC(); - case 5: - return lr.getCategory(); - case 6: - return lr.getMessage(); - case 7: - return lr.getLocation(); - case 8: - return lr.getThrownStackTrace(); - default: - String message = "The column number " + col + "must be between 0 and 8"; - throw new IllegalArgumentException(message); - } - } - - // We don't want the amount of rows to grow without bound, - // leading to a out-of-memory-exception. Especially not good - // in a production environment :) - - // This method & clearLogRecords() are synchronized so we don't - // delete rows that don't exist. - protected void trimRecords() { - if (needsTrimming()) { - trimOldestRecords(); - } - } - - protected boolean needsTrimming() { - return (_allRecords.size() > _maxNumberOfLogRecords); - } - - protected void trimOldestRecords() { - synchronized (_allRecords) { - int trim = numberOfRecordsToTrim(); - if (trim > 1) { - List oldRecords = - _allRecords.subList(0, trim); - oldRecords.clear(); - refresh(); - } else { - _allRecords.remove(0); - fastRefresh(); - } - } - - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - private int numberOfRecordsToTrim() { - return _allRecords.size() - _maxNumberOfLogRecords; - } - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} - diff --git a/java/src/org/apache/log4j/lf5/viewer/LF5SwingUtils.java b/java/src/org/apache/log4j/lf5/viewer/LF5SwingUtils.java deleted file mode 100644 index ef29447..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LF5SwingUtils.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import java.awt.Adjustable; - -import javax.swing.JComponent; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; -import javax.swing.SwingUtilities; -import javax.swing.table.TableModel; - -/** - * Provides methods to accomplish common yet non-trivial tasks - * with Swing. Obvious implementations of these methods have been - * tried and failed. - * - * @author Richard Wan - */ - -// Contributed by ThoughtWorks Inc. - -public class LF5SwingUtils { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Selects a the specified row in the specified JTable and scrolls - * the specified JScrollpane to the newly selected row. More importantly, - * the call to repaint() delayed long enough to have the table - * properly paint the newly selected row which may be offscre - * @param table should belong to the specified JScrollPane - */ - public static void selectRow(int row, JTable table, JScrollPane pane) { - if (table == null || pane == null) { - return; - } - if (contains(row, table.getModel()) == false) { - return; - } - moveAdjustable(row * table.getRowHeight(), pane.getVerticalScrollBar()); - selectRow(row, table.getSelectionModel()); - // repaint must be done later because moveAdjustable - // posts requests to the swing thread which must execute before - // the repaint logic gets executed. - repaintLater(table); - } - - /** - * Makes the specified Adjustable track if the view area expands and - * the specified Adjustable is located near the of the view. - */ - public static void makeScrollBarTrack(Adjustable scrollBar) { - if (scrollBar == null) { - return; - } - scrollBar.addAdjustmentListener(new TrackingAdjustmentListener()); - } - - /** - * Makes the vertical scroll bar of the specified JScrollPane - * track if the view expands (e.g. if rows are added to an underlying - * table). - */ - public static void makeVerticalScrollBarTrack(JScrollPane pane) { - if (pane == null) { - return; - } - makeScrollBarTrack(pane.getVerticalScrollBar()); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - protected static boolean contains(int row, TableModel model) { - if (model == null) { - return false; - } - if (row < 0) { - return false; - } - if (row >= model.getRowCount()) { - return false; - } - return true; - } - - protected static void selectRow(int row, ListSelectionModel model) { - if (model == null) { - return; - } - model.setSelectionInterval(row, row); - } - - protected static void moveAdjustable(int location, Adjustable scrollBar) { - if (scrollBar == null) { - return; - } - scrollBar.setValue(location); - } - - /** - * Work around for JTable/viewport bug. - * @link http://developer.java.sun.com/developer/bugParade/bugs/4205145.html - */ - protected static void repaintLater(final JComponent component) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - component.repaint(); - } - }); - } - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} - diff --git a/java/src/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java b/java/src/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java deleted file mode 100644 index 934ba17..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java +++ /dev/null @@ -1,1612 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.GraphicsEnvironment; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.StringTokenizer; -import java.util.Vector; - -import javax.swing.BorderFactory; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JColorChooser; -import javax.swing.JComboBox; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTextArea; -import javax.swing.JToolBar; -import javax.swing.KeyStroke; -import javax.swing.SwingUtilities; - -import org.apache.log4j.lf5.LogLevel; -import org.apache.log4j.lf5.LogRecord; -import org.apache.log4j.lf5.LogRecordFilter; -import org.apache.log4j.lf5.util.DateFormatManager; -import org.apache.log4j.lf5.util.LogFileParser; -import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerTree; -import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryPath; -import org.apache.log4j.lf5.viewer.configure.ConfigurationManager; -import org.apache.log4j.lf5.viewer.configure.MRUFileManager; - -/** - * LogBrokerMonitor - *. - * @author Michael J. Sikorsky - * @author Robert Shaw - * @author Brad Marlborough - * @author Richard Wan - * @author Brent Sprecher - * @author Richard Hurst - */ - -// Contributed by ThoughtWorks Inc. - -public class LogBrokerMonitor { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - public static final String DETAILED_VIEW = "Detailed"; -// public static final String STANDARD_VIEW = "Standard"; -// public static final String COMPACT_VIEW = "Compact"; - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected JFrame _logMonitorFrame; - protected int _logMonitorFrameWidth = 550; - protected int _logMonitorFrameHeight = 500; - protected LogTable _table; - protected CategoryExplorerTree _categoryExplorerTree; - protected String _searchText; - protected String _NDCTextFilter = ""; - protected LogLevel _leastSevereDisplayedLogLevel = LogLevel.DEBUG; - - protected JScrollPane _logTableScrollPane; - protected JLabel _statusLabel; - protected Object _lock = new Object(); - protected JComboBox _fontSizeCombo; - - protected int _fontSize = 10; - protected String _fontName = "Dialog"; - protected String _currentView = DETAILED_VIEW; - - protected boolean _loadSystemFonts = false; - protected boolean _trackTableScrollPane = true; - protected Dimension _lastTableViewportSize; - protected boolean _callSystemExitOnClose = false; - protected List _displayedLogBrokerProperties = new Vector(); - - protected Map _logLevelMenuItems = new HashMap(); - protected Map _logTableColumnMenuItems = new HashMap(); - - protected List _levels = null; - protected List _columns = null; - protected boolean _isDisposed = false; - - protected ConfigurationManager _configurationManager = null; - protected MRUFileManager _mruFileManager = null; - protected File _fileLocation = null; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - /** - * Construct a LogBrokerMonitor. - */ - public LogBrokerMonitor(List logLevels) { - - _levels = logLevels; - _columns = LogTableColumn.getLogTableColumns(); - // This allows us to use the LogBroker in command line tools and - // have the option for it to shutdown. - - String callSystemExitOnClose = - System.getProperty("monitor.exit"); - if (callSystemExitOnClose == null) { - callSystemExitOnClose = "false"; - } - callSystemExitOnClose = callSystemExitOnClose.trim().toLowerCase(); - - if (callSystemExitOnClose.equals("true")) { - _callSystemExitOnClose = true; - } - - initComponents(); - - - _logMonitorFrame.addWindowListener( - new LogBrokerMonitorWindowAdaptor(this)); - - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Show the frame for the LogBrokerMonitor. Dispatched to the - * swing thread. - */ - public void show(final int delay) { - if (_logMonitorFrame.isVisible()) { - return; - } - // This request is very low priority, let other threads execute first. - SwingUtilities.invokeLater(new Runnable() { - public void run() { - Thread.yield(); - pause(delay); - _logMonitorFrame.setVisible(true); - } - }); - } - - public void show() { - show(0); - } - - /** - * Dispose of the frame for the LogBrokerMonitor. - */ - public void dispose() { - _logMonitorFrame.dispose(); - _isDisposed = true; - - if (_callSystemExitOnClose == true) { - System.exit(0); - } - } - - /** - * Hide the frame for the LogBrokerMonitor. - */ - public void hide() { - _logMonitorFrame.setVisible(false); - } - - /** - * Get the DateFormatManager for formatting dates. - */ - public DateFormatManager getDateFormatManager() { - return _table.getDateFormatManager(); - } - - /** - * Set the date format manager for formatting dates. - */ - public void setDateFormatManager(DateFormatManager dfm) { - _table.setDateFormatManager(dfm); - } - - /** - * Get the value of whether or not System.exit() will be called - * when the LogBrokerMonitor is closed. - */ - public boolean getCallSystemExitOnClose() { - return _callSystemExitOnClose; - } - - /** - * Set the value of whether or not System.exit() will be called - * when the LogBrokerMonitor is closed. - */ - public void setCallSystemExitOnClose(boolean callSystemExitOnClose) { - _callSystemExitOnClose = callSystemExitOnClose; - } - - /** - * Add a log record message to be displayed in the LogTable. - * This method is thread-safe as it posts requests to the SwingThread - * rather than processing directly. - */ - public void addMessage(final LogRecord lr) { - if (_isDisposed == true) { - // If the frame has been disposed of, do not log any more - // messages. - return; - } - - SwingUtilities.invokeLater(new Runnable() { - public void run() { - _categoryExplorerTree.getExplorerModel().addLogRecord(lr); - _table.getFilteredLogTableModel().addLogRecord(lr); // update table - updateStatusLabel(); // show updated counts - } - }); - } - - public void setMaxNumberOfLogRecords(int maxNumberOfLogRecords) { - _table.getFilteredLogTableModel().setMaxNumberOfLogRecords(maxNumberOfLogRecords); - } - - public JFrame getBaseFrame() { - return _logMonitorFrame; - } - - public void setTitle(String title) { - _logMonitorFrame.setTitle(title + " - LogFactor5"); - } - - public void setFrameSize(int width, int height) { - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - if (0 < width && width < screen.width) { - _logMonitorFrameWidth = width; - } - if (0 < height && height < screen.height) { - _logMonitorFrameHeight = height; - } - updateFrameSize(); - } - - public void setFontSize(int fontSize) { - changeFontSizeCombo(_fontSizeCombo, fontSize); - // setFontSizeSilently(actualFontSize); - changeFontSizeCombo fires event - // refreshDetailTextArea(); - } - - public void addDisplayedProperty(Object messageLine) { - _displayedLogBrokerProperties.add(messageLine); - } - - public Map getLogLevelMenuItems() { - return _logLevelMenuItems; - } - - public Map getLogTableColumnMenuItems() { - return _logTableColumnMenuItems; - } - - public JCheckBoxMenuItem getTableColumnMenuItem(LogTableColumn column) { - return getLogTableColumnMenuItem(column); - } - - public CategoryExplorerTree getCategoryExplorerTree() { - return _categoryExplorerTree; - } - - // Added in version 1.2 - gets the value of the NDC text filter - // This value is set back to null each time the Monitor is initialized. - public String getNDCTextFilter() { - return _NDCTextFilter; - } - - // Added in version 1.2 - sets the NDC Filter based on - // a String passed in by the user. This value is persisted - // in the XML Configuration file. - public void setNDCLogRecordFilter(String textFilter) { - _table.getFilteredLogTableModel(). - setLogRecordFilter(createNDCLogRecordFilter(textFilter)); - } - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected void setSearchText(String text) { - _searchText = text; - } - - // Added in version 1.2 - Sets the text filter for the NDC - protected void setNDCTextFilter(String text) { - // if no value is set, set it to a blank string - // otherwise use the value provided - if (text == null) { - _NDCTextFilter = ""; - } else { - _NDCTextFilter = text; - } - } - - // Added in version 1.2 - Uses a different filter that sorts - // based on an NDC string passed in by the user. If the string - // is null or is an empty string, we do nothing. - protected void sortByNDC() { - String text = _NDCTextFilter; - if (text == null || text.length() == 0) { - return; - } - - // Use new NDC filter - _table.getFilteredLogTableModel(). - setLogRecordFilter(createNDCLogRecordFilter(text)); - } - - protected void findSearchText() { - String text = _searchText; - if (text == null || text.length() == 0) { - return; - } - int startRow = getFirstSelectedRow(); - int foundRow = findRecord( - startRow, - text, - _table.getFilteredLogTableModel().getFilteredRecords() - ); - selectRow(foundRow); - } - - protected int getFirstSelectedRow() { - return _table.getSelectionModel().getMinSelectionIndex(); - } - - protected void selectRow(int foundRow) { - if (foundRow == -1) { - String message = _searchText + " not found."; - JOptionPane.showMessageDialog( - _logMonitorFrame, - message, - "Text not found", - JOptionPane.INFORMATION_MESSAGE - ); - return; - } - LF5SwingUtils.selectRow(foundRow, _table, _logTableScrollPane); - } - - protected int findRecord( - int startRow, - String searchText, - List records - ) { - if (startRow < 0) { - startRow = 0; // start at first element if no rows are selected - } else { - startRow++; // start after the first selected row - } - int len = records.size(); - - for (int i = startRow; i < len; i++) { - if (matches((LogRecord) records.get(i), searchText)) { - return i; // found a record - } - } - // wrap around to beginning if when we reach the end with no match - len = startRow; - for (int i = 0; i < len; i++) { - if (matches((LogRecord) records.get(i), searchText)) { - return i; // found a record - } - } - // nothing found - return -1; - } - - /** - * Check to see if the any records contain the search string. - * Searching now supports NDC messages and date. - */ - protected boolean matches(LogRecord record, String text) { - String message = record.getMessage(); - String NDC = record.getNDC(); - - if (message == null && NDC == null || text == null) { - return false; - } - if (message.toLowerCase().indexOf(text.toLowerCase()) == -1 && - NDC.toLowerCase().indexOf(text.toLowerCase()) == -1) { - return false; - } - - return true; - } - - /** - * When the fontsize of a JTextArea is changed, the word-wrapped lines - * may become garbled. This method clears and resets the text of the - * text area. - */ - protected void refresh(JTextArea textArea) { - String text = textArea.getText(); - textArea.setText(""); - textArea.setText(text); - } - - protected void refreshDetailTextArea() { - refresh(_table._detailTextArea); - } - - protected void clearDetailTextArea() { - _table._detailTextArea.setText(""); - } - - /** - * Changes the font selection in the combo box and returns the - * size actually selected. - * @return -1 if unable to select an appropriate font - */ - protected int changeFontSizeCombo(JComboBox box, int requestedSize) { - int len = box.getItemCount(); - int currentValue; - Object currentObject; - Object selectedObject = box.getItemAt(0); - int selectedValue = Integer.parseInt(String.valueOf(selectedObject)); - for (int i = 0; i < len; i++) { - currentObject = box.getItemAt(i); - currentValue = Integer.parseInt(String.valueOf(currentObject)); - if (selectedValue < currentValue && currentValue <= requestedSize) { - selectedValue = currentValue; - selectedObject = currentObject; - } - } - box.setSelectedItem(selectedObject); - return selectedValue; - } - - /** - * Does not update gui or cause any events to be fired. - */ - protected void setFontSizeSilently(int fontSize) { - _fontSize = fontSize; - setFontSize(_table._detailTextArea, fontSize); - selectRow(0); - setFontSize(_table, fontSize); - } - - protected void setFontSize(Component component, int fontSize) { - Font oldFont = component.getFont(); - Font newFont = - new Font(oldFont.getFontName(), oldFont.getStyle(), fontSize); - component.setFont(newFont); - } - - protected void updateFrameSize() { - _logMonitorFrame.setSize(_logMonitorFrameWidth, _logMonitorFrameHeight); - centerFrame(_logMonitorFrame); - } - - protected void pause(int millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - - } - } - - protected void initComponents() { - // - // Configure the Frame. - // - _logMonitorFrame = new JFrame("LogFactor5"); - - _logMonitorFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - - String resource = - "/org/apache/log4j/lf5/viewer/images/lf5_small_icon.gif"; - URL lf5IconURL = getClass().getResource(resource); - - if (lf5IconURL != null) { - _logMonitorFrame.setIconImage(new ImageIcon(lf5IconURL).getImage()); - } - updateFrameSize(); - - // - // Configure the LogTable. - // - JTextArea detailTA = createDetailTextArea(); - JScrollPane detailTAScrollPane = new JScrollPane(detailTA); - _table = new LogTable(detailTA); - setView(_currentView, _table); - _table.setFont(new Font(_fontName, Font.PLAIN, _fontSize)); - _logTableScrollPane = new JScrollPane(_table); - - if (_trackTableScrollPane) { - _logTableScrollPane.getVerticalScrollBar().addAdjustmentListener( - new TrackingAdjustmentListener() - ); - } - - - // Configure the SplitPane between the LogTable & DetailTextArea - // - - JSplitPane tableViewerSplitPane = new JSplitPane(); - tableViewerSplitPane.setOneTouchExpandable(true); - tableViewerSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT); - tableViewerSplitPane.setLeftComponent(_logTableScrollPane); - tableViewerSplitPane.setRightComponent(detailTAScrollPane); - // Make sure to do this last.. - //tableViewerSplitPane.setDividerLocation(1.0); Doesn't work - //the same under 1.2.x & 1.3 - // "350" is a magic number that provides the correct default - // behaviour under 1.2.x & 1.3. For example, bumping this - // number to 400, causes the pane to be completely open in 1.2.x - // and closed in 1.3 - tableViewerSplitPane.setDividerLocation(350); - - // - // Configure the CategoryExplorer - // - - _categoryExplorerTree = new CategoryExplorerTree(); - - _table.getFilteredLogTableModel().setLogRecordFilter(createLogRecordFilter()); - - JScrollPane categoryExplorerTreeScrollPane = - new JScrollPane(_categoryExplorerTree); - categoryExplorerTreeScrollPane.setPreferredSize(new Dimension(130, 400)); - - // Load most recently used file list - _mruFileManager = new MRUFileManager(); - - // - // Configure the SplitPane between the CategoryExplorer & (LogTable/Detail) - // - - JSplitPane splitPane = new JSplitPane(); - splitPane.setOneTouchExpandable(true); - splitPane.setRightComponent(tableViewerSplitPane); - splitPane.setLeftComponent(categoryExplorerTreeScrollPane); - // Do this last. - splitPane.setDividerLocation(130); - // - // Add the MenuBar, StatusArea, CategoryExplorer|LogTable to the - // LogMonitorFrame. - // - _logMonitorFrame.getRootPane().setJMenuBar(createMenuBar()); - _logMonitorFrame.getContentPane().add(splitPane, BorderLayout.CENTER); - _logMonitorFrame.getContentPane().add(createToolBar(), - BorderLayout.NORTH); - _logMonitorFrame.getContentPane().add(createStatusArea(), - BorderLayout.SOUTH); - - makeLogTableListenToCategoryExplorer(); - addTableModelProperties(); - - // - // Configure ConfigurationManager - // - _configurationManager = new ConfigurationManager(this, _table); - - } - - protected LogRecordFilter createLogRecordFilter() { - LogRecordFilter result = new LogRecordFilter() { - public boolean passes(LogRecord record) { - CategoryPath path = new CategoryPath(record.getCategory()); - return - getMenuItem(record.getLevel()).isSelected() && - _categoryExplorerTree.getExplorerModel().isCategoryPathActive(path); - } - }; - return result; - } - - // Added in version 1.2 - Creates a new filter that sorts records based on - // an NDC string passed in by the user. - protected LogRecordFilter createNDCLogRecordFilter(String text) { - _NDCTextFilter = text; - LogRecordFilter result = new LogRecordFilter() { - public boolean passes(LogRecord record) { - String NDC = record.getNDC(); - CategoryPath path = new CategoryPath(record.getCategory()); - if (NDC == null || _NDCTextFilter == null) { - return false; - } else if (NDC.toLowerCase().indexOf(_NDCTextFilter.toLowerCase()) == -1) { - return false; - } else { - return getMenuItem(record.getLevel()).isSelected() && - _categoryExplorerTree.getExplorerModel().isCategoryPathActive(path); - } - } - }; - - return result; - } - - - protected void updateStatusLabel() { - _statusLabel.setText(getRecordsDisplayedMessage()); - } - - protected String getRecordsDisplayedMessage() { - FilteredLogTableModel model = _table.getFilteredLogTableModel(); - return getStatusText(model.getRowCount(), model.getTotalRowCount()); - } - - protected void addTableModelProperties() { - final FilteredLogTableModel model = _table.getFilteredLogTableModel(); - - addDisplayedProperty(new Object() { - public String toString() { - return getRecordsDisplayedMessage(); - } - }); - addDisplayedProperty(new Object() { - public String toString() { - return "Maximum number of displayed LogRecords: " - + model._maxNumberOfLogRecords; - } - }); - } - - protected String getStatusText(int displayedRows, int totalRows) { - StringBuffer result = new StringBuffer(); - result.append("Displaying: "); - result.append(displayedRows); - result.append(" records out of a total of: "); - result.append(totalRows); - result.append(" records."); - return result.toString(); - } - - protected void makeLogTableListenToCategoryExplorer() { - ActionListener listener = new ActionListener() { - public void actionPerformed(ActionEvent e) { - _table.getFilteredLogTableModel().refresh(); - updateStatusLabel(); - } - }; - _categoryExplorerTree.getExplorerModel().addActionListener(listener); - } - - protected JPanel createStatusArea() { - JPanel statusArea = new JPanel(); - JLabel status = - new JLabel("No log records to display."); - _statusLabel = status; - status.setHorizontalAlignment(JLabel.LEFT); - - statusArea.setBorder(BorderFactory.createEtchedBorder()); - statusArea.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); - statusArea.add(status); - - return (statusArea); - } - - protected JTextArea createDetailTextArea() { - JTextArea detailTA = new JTextArea(); - detailTA.setFont(new Font("Monospaced", Font.PLAIN, 14)); - detailTA.setTabSize(3); - detailTA.setLineWrap(true); - detailTA.setWrapStyleWord(false); - return (detailTA); - } - - protected JMenuBar createMenuBar() { - JMenuBar menuBar = new JMenuBar(); - menuBar.add(createFileMenu()); - menuBar.add(createEditMenu()); - menuBar.add(createLogLevelMenu()); - menuBar.add(createViewMenu()); - menuBar.add(createConfigureMenu()); - menuBar.add(createHelpMenu()); - - return (menuBar); - } - - protected JMenu createLogLevelMenu() { - JMenu result = new JMenu("Log Level"); - result.setMnemonic('l'); - Iterator levels = getLogLevels(); - while (levels.hasNext()) { - result.add(getMenuItem((LogLevel) levels.next())); - } - - result.addSeparator(); - result.add(createAllLogLevelsMenuItem()); - result.add(createNoLogLevelsMenuItem()); - result.addSeparator(); - result.add(createLogLevelColorMenu()); - result.add(createResetLogLevelColorMenuItem()); - - return result; - } - - protected JMenuItem createAllLogLevelsMenuItem() { - JMenuItem result = new JMenuItem("Show all LogLevels"); - result.setMnemonic('s'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - selectAllLogLevels(true); - _table.getFilteredLogTableModel().refresh(); - updateStatusLabel(); - } - }); - return result; - } - - protected JMenuItem createNoLogLevelsMenuItem() { - JMenuItem result = new JMenuItem("Hide all LogLevels"); - result.setMnemonic('h'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - selectAllLogLevels(false); - _table.getFilteredLogTableModel().refresh(); - updateStatusLabel(); - } - }); - return result; - } - - protected JMenu createLogLevelColorMenu() { - JMenu colorMenu = new JMenu("Configure LogLevel Colors"); - colorMenu.setMnemonic('c'); - Iterator levels = getLogLevels(); - while (levels.hasNext()) { - colorMenu.add(createSubMenuItem((LogLevel) levels.next())); - } - - return colorMenu; - } - - protected JMenuItem createResetLogLevelColorMenuItem() { - JMenuItem result = new JMenuItem("Reset LogLevel Colors"); - result.setMnemonic('r'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - // reset the level colors in the map - LogLevel.resetLogLevelColorMap(); - - // refresh the table - _table.getFilteredLogTableModel().refresh(); - } - }); - return result; - } - - protected void selectAllLogLevels(boolean selected) { - Iterator levels = getLogLevels(); - while (levels.hasNext()) { - getMenuItem((LogLevel) levels.next()).setSelected(selected); - } - } - - protected JCheckBoxMenuItem getMenuItem(LogLevel level) { - JCheckBoxMenuItem result = (JCheckBoxMenuItem) (_logLevelMenuItems.get(level)); - if (result == null) { - result = createMenuItem(level); - _logLevelMenuItems.put(level, result); - } - return result; - } - - protected JMenuItem createSubMenuItem(LogLevel level) { - final JMenuItem result = new JMenuItem(level.toString()); - final LogLevel logLevel = level; - result.setMnemonic(level.toString().charAt(0)); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - showLogLevelColorChangeDialog(result, logLevel); - } - }); - - return result; - - } - - protected void showLogLevelColorChangeDialog(JMenuItem result, LogLevel level) { - JMenuItem menuItem = result; - Color newColor = JColorChooser.showDialog( - _logMonitorFrame, - "Choose LogLevel Color", - result.getForeground()); - - if (newColor != null) { - // set the color for the record - level.setLogLevelColorMap(level, newColor); - _table.getFilteredLogTableModel().refresh(); - } - - } - - protected JCheckBoxMenuItem createMenuItem(LogLevel level) { - JCheckBoxMenuItem result = new JCheckBoxMenuItem(level.toString()); - result.setSelected(true); - result.setMnemonic(level.toString().charAt(0)); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - _table.getFilteredLogTableModel().refresh(); - updateStatusLabel(); - } - }); - return result; - } - - // view menu - protected JMenu createViewMenu() { - JMenu result = new JMenu("View"); - result.setMnemonic('v'); - Iterator columns = getLogTableColumns(); - while (columns.hasNext()) { - result.add(getLogTableColumnMenuItem((LogTableColumn) columns.next())); - } - - result.addSeparator(); - result.add(createAllLogTableColumnsMenuItem()); - result.add(createNoLogTableColumnsMenuItem()); - return result; - } - - protected JCheckBoxMenuItem getLogTableColumnMenuItem(LogTableColumn column) { - JCheckBoxMenuItem result = (JCheckBoxMenuItem) (_logTableColumnMenuItems.get(column)); - if (result == null) { - result = createLogTableColumnMenuItem(column); - _logTableColumnMenuItems.put(column, result); - } - return result; - } - - protected JCheckBoxMenuItem createLogTableColumnMenuItem(LogTableColumn column) { - JCheckBoxMenuItem result = new JCheckBoxMenuItem(column.toString()); - - result.setSelected(true); - result.setMnemonic(column.toString().charAt(0)); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - // update list of columns and reset the view - List selectedColumns = updateView(); - _table.setView(selectedColumns); - } - }); - return result; - } - - protected List updateView() { - ArrayList updatedList = new ArrayList(); - Iterator columnIterator = _columns.iterator(); - while (columnIterator.hasNext()) { - LogTableColumn column = (LogTableColumn) columnIterator.next(); - JCheckBoxMenuItem result = getLogTableColumnMenuItem(column); - // check and see if the checkbox is checked - if (result.isSelected()) { - updatedList.add(column); - } - } - - return updatedList; - } - - protected JMenuItem createAllLogTableColumnsMenuItem() { - JMenuItem result = new JMenuItem("Show all Columns"); - result.setMnemonic('s'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - selectAllLogTableColumns(true); - // update list of columns and reset the view - List selectedColumns = updateView(); - _table.setView(selectedColumns); - } - }); - return result; - } - - protected JMenuItem createNoLogTableColumnsMenuItem() { - JMenuItem result = new JMenuItem("Hide all Columns"); - result.setMnemonic('h'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - selectAllLogTableColumns(false); - // update list of columns and reset the view - List selectedColumns = updateView(); - _table.setView(selectedColumns); - } - }); - return result; - } - - protected void selectAllLogTableColumns(boolean selected) { - Iterator columns = getLogTableColumns(); - while (columns.hasNext()) { - getLogTableColumnMenuItem((LogTableColumn) columns.next()).setSelected(selected); - } - } - - protected JMenu createFileMenu() { - JMenu fileMenu = new JMenu("File"); - fileMenu.setMnemonic('f'); - JMenuItem exitMI; - fileMenu.add(createOpenMI()); - fileMenu.add(createOpenURLMI()); - fileMenu.addSeparator(); - fileMenu.add(createCloseMI()); - createMRUFileListMI(fileMenu); - fileMenu.addSeparator(); - fileMenu.add(createExitMI()); - return fileMenu; - } - - /** - * Menu item added to allow log files to be opened with - * the LF5 GUI. - */ - protected JMenuItem createOpenMI() { - JMenuItem result = new JMenuItem("Open..."); - result.setMnemonic('o'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - requestOpen(); - } - }); - return result; - } - - /** - * Menu item added to allow log files loaded from a URL - * to be opened by the LF5 GUI. - */ - protected JMenuItem createOpenURLMI() { - JMenuItem result = new JMenuItem("Open URL..."); - result.setMnemonic('u'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - requestOpenURL(); - } - }); - return result; - } - - protected JMenuItem createCloseMI() { - JMenuItem result = new JMenuItem("Close"); - result.setMnemonic('c'); - result.setAccelerator(KeyStroke.getKeyStroke("control Q")); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - requestClose(); - } - }); - return result; - } - - /** - * Creates a Most Recently Used file list to be - * displayed in the File menu - */ - protected void createMRUFileListMI(JMenu menu) { - - String[] files = _mruFileManager.getMRUFileList(); - - if (files != null) { - menu.addSeparator(); - for (int i = 0; i < files.length; i++) { - JMenuItem result = new JMenuItem((i + 1) + " " + files[i]); - result.setMnemonic(i + 1); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - requestOpenMRU(e); - } - }); - menu.add(result); - } - } - } - - protected JMenuItem createExitMI() { - JMenuItem result = new JMenuItem("Exit"); - result.setMnemonic('x'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - requestExit(); - } - }); - return result; - } - - protected JMenu createConfigureMenu() { - JMenu configureMenu = new JMenu("Configure"); - configureMenu.setMnemonic('c'); - configureMenu.add(createConfigureSave()); - configureMenu.add(createConfigureReset()); - configureMenu.add(createConfigureMaxRecords()); - - return configureMenu; - } - - protected JMenuItem createConfigureSave() { - JMenuItem result = new JMenuItem("Save"); - result.setMnemonic('s'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - saveConfiguration(); - } - }); - - return result; - } - - protected JMenuItem createConfigureReset() { - JMenuItem result = new JMenuItem("Reset"); - result.setMnemonic('r'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - resetConfiguration(); - } - }); - - return result; - } - - protected JMenuItem createConfigureMaxRecords() { - JMenuItem result = new JMenuItem("Set Max Number of Records"); - result.setMnemonic('m'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - setMaxRecordConfiguration(); - } - }); - - return result; - } - - - protected void saveConfiguration() { - _configurationManager.save(); - } - - protected void resetConfiguration() { - _configurationManager.reset(); - } - - protected void setMaxRecordConfiguration() { - LogFactor5InputDialog inputDialog = new LogFactor5InputDialog( - getBaseFrame(), "Set Max Number of Records", "", 10); - - String temp = inputDialog.getText(); - - if (temp != null) { - try { - setMaxNumberOfLogRecords(Integer.parseInt(temp)); - } catch (NumberFormatException e) { - LogFactor5ErrorDialog error = new LogFactor5ErrorDialog( - getBaseFrame(), - "'" + temp + "' is an invalid parameter.\nPlease try again."); - setMaxRecordConfiguration(); - } - } - } - - - protected JMenu createHelpMenu() { - JMenu helpMenu = new JMenu("Help"); - helpMenu.setMnemonic('h'); - helpMenu.add(createHelpProperties()); - return helpMenu; - } - - protected JMenuItem createHelpProperties() { - final String title = "LogFactor5 Properties"; - final JMenuItem result = new JMenuItem(title); - result.setMnemonic('l'); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - showPropertiesDialog(title); - } - }); - return result; - } - - protected void showPropertiesDialog(String title) { - JOptionPane.showMessageDialog( - _logMonitorFrame, - _displayedLogBrokerProperties.toArray(), - title, - JOptionPane.PLAIN_MESSAGE - ); - } - - protected JMenu createEditMenu() { - JMenu editMenu = new JMenu("Edit"); - editMenu.setMnemonic('e'); - editMenu.add(createEditFindMI()); - editMenu.add(createEditFindNextMI()); - editMenu.addSeparator(); - editMenu.add(createEditSortNDCMI()); - editMenu.add(createEditRestoreAllNDCMI()); - return editMenu; - } - - protected JMenuItem createEditFindNextMI() { - JMenuItem editFindNextMI = new JMenuItem("Find Next"); - editFindNextMI.setMnemonic('n'); - editFindNextMI.setAccelerator(KeyStroke.getKeyStroke("F3")); - editFindNextMI.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - findSearchText(); - } - }); - return editFindNextMI; - } - - protected JMenuItem createEditFindMI() { - JMenuItem editFindMI = new JMenuItem("Find"); - editFindMI.setMnemonic('f'); - editFindMI.setAccelerator(KeyStroke.getKeyStroke("control F")); - - editFindMI.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - String inputValue = - JOptionPane.showInputDialog( - _logMonitorFrame, - "Find text: ", - "Search Record Messages", - JOptionPane.QUESTION_MESSAGE - ); - setSearchText(inputValue); - findSearchText(); - } - } - - ); - return editFindMI; - } - - // Added version 1.2 - Allows users to Sort Log Records by an - // NDC text filter. A new LogRecordFilter was created to - // sort the records. - protected JMenuItem createEditSortNDCMI() { - JMenuItem editSortNDCMI = new JMenuItem("Sort by NDC"); - editSortNDCMI.setMnemonic('s'); - editSortNDCMI.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - String inputValue = - JOptionPane.showInputDialog( - _logMonitorFrame, - "Sort by this NDC: ", - "Sort Log Records by NDC", - JOptionPane.QUESTION_MESSAGE - ); - setNDCTextFilter(inputValue); - sortByNDC(); - _table.getFilteredLogTableModel().refresh(); - updateStatusLabel(); - } - } - - ); - return editSortNDCMI; - } - - // Added in version 1.2 - Resets the LogRecordFilter back to default - // filter. - protected JMenuItem createEditRestoreAllNDCMI() { - JMenuItem editRestoreAllNDCMI = new JMenuItem("Restore all NDCs"); - editRestoreAllNDCMI.setMnemonic('r'); - editRestoreAllNDCMI.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - _table.getFilteredLogTableModel().setLogRecordFilter(createLogRecordFilter()); - // reset the text filter - setNDCTextFilter(""); - _table.getFilteredLogTableModel().refresh(); - updateStatusLabel(); - } - } - ); - return editRestoreAllNDCMI; - } - - protected JToolBar createToolBar() { - JToolBar tb = new JToolBar(); - tb.putClientProperty("JToolBar.isRollover", Boolean.TRUE); - JComboBox fontCombo = new JComboBox(); - JComboBox fontSizeCombo = new JComboBox(); - _fontSizeCombo = fontSizeCombo; - - ClassLoader cl = this.getClass().getClassLoader(); - if(cl == null) { - cl = ClassLoader.getSystemClassLoader(); - } - URL newIconURL = cl.getResource("org/apache/log4j/lf5/viewer/" + - "images/channelexplorer_new.gif"); - - ImageIcon newIcon = null; - - if (newIconURL != null) { - newIcon = new ImageIcon(newIconURL); - } - - JButton newButton = new JButton("Clear Log Table"); - - if (newIcon != null) { - newButton.setIcon(newIcon); - } - - newButton.setToolTipText("Clear Log Table."); - //newButton.setBorder(BorderFactory.createEtchedBorder()); - - newButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - _table.clearLogRecords(); - _categoryExplorerTree.getExplorerModel().resetAllNodeCounts(); - updateStatusLabel(); - clearDetailTextArea(); - LogRecord.resetSequenceNumber(); - } - } - ); - - Toolkit tk = Toolkit.getDefaultToolkit(); - // This will actually grab all the fonts - - String[] fonts; - - if (_loadSystemFonts) { - fonts = GraphicsEnvironment. - getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); - } else { - fonts = tk.getFontList(); - } - - for (int j = 0; j < fonts.length; j++) { - fontCombo.addItem(fonts[j]); - } - - fontCombo.setSelectedItem(_fontName); - - fontCombo.addActionListener( - - new ActionListener() { - public void actionPerformed(ActionEvent e) { - JComboBox box = (JComboBox) e.getSource(); - String font = (String) box.getSelectedItem(); - _table.setFont(new Font(font, Font.PLAIN, _fontSize)); - _fontName = font; - } - } - ); - - fontSizeCombo.addItem("8"); - fontSizeCombo.addItem("9"); - fontSizeCombo.addItem("10"); - fontSizeCombo.addItem("12"); - fontSizeCombo.addItem("14"); - fontSizeCombo.addItem("16"); - fontSizeCombo.addItem("18"); - fontSizeCombo.addItem("24"); - - fontSizeCombo.setSelectedItem(String.valueOf(_fontSize)); - fontSizeCombo.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - JComboBox box = (JComboBox) e.getSource(); - String size = (String) box.getSelectedItem(); - int s = Integer.valueOf(size).intValue(); - - setFontSizeSilently(s); - refreshDetailTextArea(); - _fontSize = s; - } - } - ); - - tb.add(new JLabel(" Font: ")); - tb.add(fontCombo); - tb.add(fontSizeCombo); - tb.addSeparator(); - tb.addSeparator(); - tb.add(newButton); - - newButton.setAlignmentY(0.5f); - newButton.setAlignmentX(0.5f); - - fontCombo.setMaximumSize(fontCombo.getPreferredSize()); - fontSizeCombo.setMaximumSize( - fontSizeCombo.getPreferredSize()); - - return (tb); - } - -// protected void setView(String viewString, LogTable table) { -// if (STANDARD_VIEW.equals(viewString)) { -// table.setStandardView(); -// } else if (COMPACT_VIEW.equals(viewString)) { -// table.setCompactView(); -// } else if (DETAILED_VIEW.equals(viewString)) { -// table.setDetailedView(); -// } else { -// String message = viewString + "does not match a supported view."; -// throw new IllegalArgumentException(message); -// } -// _currentView = viewString; -// } - - protected void setView(String viewString, LogTable table) { - if (DETAILED_VIEW.equals(viewString)) { - table.setDetailedView(); - } else { - String message = viewString + "does not match a supported view."; - throw new IllegalArgumentException(message); - } - _currentView = viewString; - } - - protected JComboBox createLogLevelCombo() { - JComboBox result = new JComboBox(); - Iterator levels = getLogLevels(); - while (levels.hasNext()) { - result.addItem(levels.next()); - } - result.setSelectedItem(_leastSevereDisplayedLogLevel); - - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JComboBox box = (JComboBox) e.getSource(); - LogLevel level = (LogLevel) box.getSelectedItem(); - setLeastSevereDisplayedLogLevel(level); - } - }); - result.setMaximumSize(result.getPreferredSize()); - return result; - } - - protected void setLeastSevereDisplayedLogLevel(LogLevel level) { - if (level == null || _leastSevereDisplayedLogLevel == level) { - return; // nothing to do - } - _leastSevereDisplayedLogLevel = level; - _table.getFilteredLogTableModel().refresh(); - updateStatusLabel(); - } - - /** - * Ensures that the Table's ScrollPane Viewport will "track" with updates - * to the Table. When the vertical scroll bar is at its bottom anchor - * and tracking is enabled then viewport will stay at the bottom most - * point of the component. The purpose of this feature is to allow - * a developer to watch the table as messages arrive and not have to - * scroll after each new message arrives. When the vertical scroll bar - * is at any other location, then no tracking will happen. - * @deprecated tracking is now done automatically. - */ - protected void trackTableScrollPane() { - // do nothing - } - - protected void centerFrame(JFrame frame) { - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - Dimension comp = frame.getSize(); - - frame.setLocation(((screen.width - comp.width) / 2), - ((screen.height - comp.height) / 2)); - - } - - /** - * Uses a JFileChooser to select a file to opened with the - * LF5 GUI. - */ - protected void requestOpen() { - JFileChooser chooser; - - if (_fileLocation == null) { - chooser = new JFileChooser(); - } else { - chooser = new JFileChooser(_fileLocation); - } - - int returnVal = chooser.showOpenDialog(_logMonitorFrame); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File f = chooser.getSelectedFile(); - if (loadLogFile(f)) { - _fileLocation = chooser.getSelectedFile(); - _mruFileManager.set(f); - updateMRUList(); - } - } - } - - /** - * Uses a Dialog box to accept a URL to a file to be opened - * with the LF5 GUI. - */ - protected void requestOpenURL() { - LogFactor5InputDialog inputDialog = new LogFactor5InputDialog( - getBaseFrame(), "Open URL", "URL:"); - String temp = inputDialog.getText(); - - if (temp != null) { - if (temp.indexOf("://") == -1) { - temp = "http://" + temp; - } - - try { - URL url = new URL(temp); - if (loadLogFile(url)) { - _mruFileManager.set(url); - updateMRUList(); - } - } catch (MalformedURLException e) { - LogFactor5ErrorDialog error = new LogFactor5ErrorDialog( - getBaseFrame(), "Error reading URL."); - } - } - } - - /** - * Removes old file list and creates a new file list - * with the updated MRU list. - */ - protected void updateMRUList() { - JMenu menu = _logMonitorFrame.getJMenuBar().getMenu(0); - menu.removeAll(); - menu.add(createOpenMI()); - menu.add(createOpenURLMI()); - menu.addSeparator(); - menu.add(createCloseMI()); - createMRUFileListMI(menu); - menu.addSeparator(); - menu.add(createExitMI()); - } - - protected void requestClose() { - setCallSystemExitOnClose(false); - closeAfterConfirm(); - } - - /** - * Opens a file in the MRU list. - */ - protected void requestOpenMRU(ActionEvent e) { - String file = e.getActionCommand(); - StringTokenizer st = new StringTokenizer(file); - String num = st.nextToken().trim(); - file = st.nextToken("\n"); - - try { - int index = Integer.parseInt(num) - 1; - - InputStream in = _mruFileManager.getInputStream(index); - LogFileParser lfp = new LogFileParser(in); - lfp.parse(this); - - _mruFileManager.moveToTop(index); - updateMRUList(); - - } catch (Exception me) { - LogFactor5ErrorDialog error = new LogFactor5ErrorDialog( - getBaseFrame(), "Unable to load file " + file); - } - - } - - protected void requestExit() { - _mruFileManager.save(); - setCallSystemExitOnClose(true); - closeAfterConfirm(); - } - - protected void closeAfterConfirm() { - StringBuffer message = new StringBuffer(); - - if (_callSystemExitOnClose == false) { - message.append("Are you sure you want to close the logging "); - message.append("console?\n"); - message.append("(Note: This will not shut down the Virtual Machine,\n"); - message.append("or the Swing event thread.)"); - } else { - message.append("Are you sure you want to exit?\n"); - message.append("This will shut down the Virtual Machine.\n"); - } - - String title = - "Are you sure you want to dispose of the Logging Console?"; - - if (_callSystemExitOnClose == true) { - title = "Are you sure you want to exit?"; - } - int value = JOptionPane.showConfirmDialog( - _logMonitorFrame, - message.toString(), - title, - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE, - null - ); - - if (value == JOptionPane.OK_OPTION) { - dispose(); - } - } - - protected Iterator getLogLevels() { - return _levels.iterator(); - } - - protected Iterator getLogTableColumns() { - return _columns.iterator(); - } - - /** - * Loads and parses a log file. - */ - protected boolean loadLogFile(File file) { - boolean ok = false; - try { - LogFileParser lfp = new LogFileParser(file); - lfp.parse(this); - ok = true; - } catch (IOException e) { - LogFactor5ErrorDialog error = new LogFactor5ErrorDialog( - getBaseFrame(), "Error reading " + file.getName()); - } - - return ok; - } - - /** - * Loads a parses a log file running on a server. - */ - protected boolean loadLogFile(URL url) { - boolean ok = false; - try { - LogFileParser lfp = new LogFileParser(url.openStream()); - lfp.parse(this); - ok = true; - } catch (IOException e) { - LogFactor5ErrorDialog error = new LogFactor5ErrorDialog( - getBaseFrame(), "Error reading URL:" + url.getFile()); - } - return ok; - } - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - - class LogBrokerMonitorWindowAdaptor extends WindowAdapter { - protected LogBrokerMonitor _monitor; - - public LogBrokerMonitorWindowAdaptor(LogBrokerMonitor monitor) { - _monitor = monitor; - } - - public void windowClosing(WindowEvent ev) { - _monitor.requestClose(); - } - } -} - - diff --git a/java/src/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java b/java/src/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java deleted file mode 100644 index 763d870..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.Label; -import java.awt.Toolkit; -import java.awt.Window; - -import javax.swing.JDialog; -import javax.swing.JFrame; - -/** - * LogFactor5Dialog - * - * @author Richard Hurst - * @author Brad Marlborough - */ - -// Contributed by ThoughtWorks Inc. - -public abstract class LogFactor5Dialog extends JDialog { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - protected static final Font DISPLAY_FONT = new Font("Arial", Font.BOLD, 12); - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - protected LogFactor5Dialog(JFrame jframe, String message, boolean modal) { - super(jframe, message, modal); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - public void show() { - pack(); - minimumSizeDialog(this, 200, 100); - centerWindow(this); - super.show(); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - protected void centerWindow(Window win) { - Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize(); - - // If larger than screen, reduce window width or height - if (screenDim.width < win.getSize().width) { - win.setSize(screenDim.width, win.getSize().height); - } - - if (screenDim.height < win.getSize().height) { - win.setSize(win.getSize().width, screenDim.height); - } - - // Center Frame, Dialogue or Window on screen - int x = (screenDim.width - win.getSize().width) / 2; - int y = (screenDim.height - win.getSize().height) / 2; - win.setLocation(x, y); - } - - protected void wrapStringOnPanel(String message, - Container container) { - GridBagConstraints c = getDefaultConstraints(); - c.gridwidth = GridBagConstraints.REMAINDER; - // Insets() args are top, left, bottom, right - c.insets = new Insets(0, 0, 0, 0); - GridBagLayout gbLayout = (GridBagLayout) container.getLayout(); - - - while (message.length() > 0) { - int newLineIndex = message.indexOf('\n'); - String line; - if (newLineIndex >= 0) { - line = message.substring(0, newLineIndex); - message = message.substring(newLineIndex + 1); - } else { - line = message; - message = ""; - } - Label label = new Label(line); - label.setFont(DISPLAY_FONT); - gbLayout.setConstraints(label, c); - container.add(label); - } - } - - protected GridBagConstraints getDefaultConstraints() { - GridBagConstraints constraints = new GridBagConstraints(); - constraints.weightx = 1.0; - constraints.weighty = 1.0; - constraints.gridheight = 1; // One row high - // Insets() args are top, left, bottom, right - constraints.insets = new Insets(4, 4, 4, 4); - // fill of NONE means do not change size - constraints.fill = GridBagConstraints.NONE; - // WEST means align left - constraints.anchor = GridBagConstraints.WEST; - - return constraints; - } - - protected void minimumSizeDialog(Component component, - int minWidth, - int minHeight) { - // set the min width - if (component.getSize().width < minWidth) - component.setSize(minWidth, component.getSize().height); - // set the min height - if (component.getSize().height < minHeight) - component.setSize(component.getSize().width, minHeight); - } - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} \ No newline at end of file diff --git a/java/src/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java b/java/src/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java deleted file mode 100644 index 64b2e21..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import java.awt.BorderLayout; -import java.awt.FlowLayout; -import java.awt.GridBagLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JPanel; - -/** - * LogFactor5ErrorDialog - * - * @author Richard Hurst - * @author Brad Marlborough - */ - -// Contributed by ThoughtWorks Inc. - -public class LogFactor5ErrorDialog extends LogFactor5Dialog { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - public LogFactor5ErrorDialog(JFrame jframe, String message) { - super(jframe, "Error", true); - - JButton ok = new JButton("Ok"); - ok.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - hide(); - } - }); - - JPanel bottom = new JPanel(); - bottom.setLayout(new FlowLayout()); - bottom.add(ok); - - JPanel main = new JPanel(); - main.setLayout(new GridBagLayout()); - wrapStringOnPanel(message, main); - - getContentPane().add(main, BorderLayout.CENTER); - getContentPane().add(bottom, BorderLayout.SOUTH); - show(); - - } - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} \ No newline at end of file diff --git a/java/src/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java b/java/src/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java deleted file mode 100644 index 890e6db..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; - -/** - * LogFactor5InputDialog - * - * Creates a popup input dialog box so that users can enter - * a URL to open a log file from. - * - * @author Richard Hurst - * @author Brad Marlborough - */ - -// Contributed by ThoughtWorks Inc. - -public class LogFactor5InputDialog extends LogFactor5Dialog { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - public static final int SIZE = 30; - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - private JTextField _textField; - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - /** - * Configures an input dialog box using a defualt size for the text field. - * param jframe the frame where the dialog will be loaded from. - * param title the title of the dialog box. - * param label the label to be put in the dialog box. - */ - public LogFactor5InputDialog(JFrame jframe, String title, String label) { - this(jframe, title, label, SIZE); - } - - /** - * Configures an input dialog box. - * param jframe the frame where the dialog will be loaded from. - * param title the title of the dialog box. - * param label the label to be put in the dialog box. - * param size the size of the text field. - */ - public LogFactor5InputDialog(JFrame jframe, String title, String label, - int size) { - super(jframe, title, true); - - JPanel bottom = new JPanel(); - bottom.setLayout(new FlowLayout()); - - JPanel main = new JPanel(); - main.setLayout(new FlowLayout()); - main.add(new JLabel(label)); - _textField = new JTextField(size); - main.add(_textField); - - addKeyListener(new KeyAdapter() { - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - hide(); - } - } - }); - - JButton ok = new JButton("Ok"); - ok.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - hide(); - } - }); - - JButton cancel = new JButton("Cancel"); - cancel.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - hide(); - // set the text field to blank just in case - // a file was selected before the Cancel - // button was pressed. - _textField.setText(""); - } - }); - - bottom.add(ok); - bottom.add(cancel); - getContentPane().add(main, BorderLayout.CENTER); - getContentPane().add(bottom, BorderLayout.SOUTH); - pack(); - centerWindow(this); - show(); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - public String getText() { - String s = _textField.getText(); - - if (s != null && s.trim().length() == 0) { - return null; - } - - return s; - - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} \ No newline at end of file diff --git a/java/src/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java b/java/src/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java deleted file mode 100644 index 3e5a62b..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import java.awt.BorderLayout; -import java.awt.FlowLayout; -import java.awt.GridBagLayout; - -import javax.swing.JFrame; -import javax.swing.JPanel; - -/** - * LogFactor5LoadingDialog - * - * @author Richard Hurst - * @author Brad Marlborough - */ - -// Contributed by ThoughtWorks Inc. - -public class LogFactor5LoadingDialog extends LogFactor5Dialog { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public LogFactor5LoadingDialog(JFrame jframe, String message) { - super(jframe, "LogFactor5", false); - - JPanel bottom = new JPanel(); - bottom.setLayout(new FlowLayout()); - - JPanel main = new JPanel(); - main.setLayout(new GridBagLayout()); - wrapStringOnPanel(message, main); - - getContentPane().add(main, BorderLayout.CENTER); - getContentPane().add(bottom, BorderLayout.SOUTH); - show(); - - } - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} \ No newline at end of file diff --git a/java/src/org/apache/log4j/lf5/viewer/LogTable.java b/java/src/org/apache/log4j/lf5/viewer/LogTable.java deleted file mode 100644 index 6bea10d..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LogTable.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import java.util.Vector; - -import javax.swing.JTable; -import javax.swing.JTextArea; -import javax.swing.ListSelectionModel; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; - -import org.apache.log4j.lf5.util.DateFormatManager; - -/** - * LogTable. - * - * @author Michael J. Sikorsky - * @author Robert Shaw - * @author Brad Marlborough - * @author Brent Sprecher - */ - -// Contributed by ThoughtWorks Inc. - -public class LogTable extends JTable { - private static final long serialVersionUID = 4867085140195148458L; - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected int _rowHeight = 30; - protected JTextArea _detailTextArea; - - // For the columns: - protected int _numCols = 9; - protected TableColumn[] _tableColumns = new TableColumn[_numCols]; - protected int[] _colWidths = {40, 40, 40, 70, 70, 360, 440, 200, 60}; - protected LogTableColumn[] _colNames = LogTableColumn.getLogTableColumnArray(); - protected int _colDate = 0; - protected int _colThread = 1; - protected int _colMessageNum = 2; - protected int _colLevel = 3; - protected int _colNDC = 4; - protected int _colCategory = 5; - protected int _colMessage = 6; - protected int _colLocation = 7; - protected int _colThrown = 8; - - protected DateFormatManager _dateFormatManager = null; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public LogTable(JTextArea detailTextArea) { - super(); - - init(); - - _detailTextArea = detailTextArea; - - setModel(new FilteredLogTableModel()); - - Enumeration columns = getColumnModel().getColumns(); - int i = 0; - while (columns.hasMoreElements()) { - TableColumn col = (TableColumn) columns.nextElement(); - col.setCellRenderer(new LogTableRowRenderer()); - col.setPreferredWidth(_colWidths[i]); - - _tableColumns[i] = col; - i++; - } - - ListSelectionModel rowSM = getSelectionModel(); - rowSM.addListSelectionListener(new LogTableListSelectionListener(this)); - - //setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Get the DateFormatManager for formatting dates. - */ - public DateFormatManager getDateFormatManager() { - return _dateFormatManager; - } - - /** - * Set the date format manager for formatting dates. - */ - public void setDateFormatManager(DateFormatManager dfm) { - _dateFormatManager = dfm; - } - - public synchronized void clearLogRecords() { - //For JDK1.3 - //((DefaultTableModel)getModel()).setRowCount(0); - - // For JDK1.2.x - getFilteredLogTableModel().clear(); - } - - public FilteredLogTableModel getFilteredLogTableModel() { - return (FilteredLogTableModel) getModel(); - } - - // default view if a view is not set and saved - public void setDetailedView() { - //TODO: Defineable Views. - TableColumnModel model = getColumnModel(); - // Remove all the columns: - for (int f = 0; f < _numCols; f++) { - model.removeColumn(_tableColumns[f]); - } - // Add them back in the correct order: - for (int i = 0; i < _numCols; i++) { - model.addColumn(_tableColumns[i]); - } - //SWING BUG: - sizeColumnsToFit(-1); - } - - public void setView(List columns) { - TableColumnModel model = getColumnModel(); - - // Remove all the columns: - for (int f = 0; f < _numCols; f++) { - model.removeColumn(_tableColumns[f]); - } - Iterator selectedColumns = columns.iterator(); - Vector columnNameAndNumber = getColumnNameAndNumber(); - while (selectedColumns.hasNext()) { - // add the column to the view - model.addColumn(_tableColumns[columnNameAndNumber.indexOf(selectedColumns.next())]); - } - - //SWING BUG: - sizeColumnsToFit(-1); - } - - public void setFont(Font font) { - super.setFont(font); - Graphics g = this.getGraphics(); - if (g != null) { - FontMetrics fm = g.getFontMetrics(font); - int height = fm.getHeight(); - _rowHeight = height + height / 3; - setRowHeight(_rowHeight); - } - - - } - - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected void init() { - setRowHeight(_rowHeight); - setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - } - - // assign a column number to a column name - protected Vector getColumnNameAndNumber() { - Vector columnNameAndNumber = new Vector(); - for (int i = 0; i < _colNames.length; i++) { - columnNameAndNumber.add(i, _colNames[i]); - } - return columnNameAndNumber; - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - - class LogTableListSelectionListener implements ListSelectionListener { - protected JTable _table; - - public LogTableListSelectionListener(JTable table) { - _table = table; - } - - public void valueChanged(ListSelectionEvent e) { - //Ignore extra messages. - if (e.getValueIsAdjusting()) { - return; - } - - ListSelectionModel lsm = (ListSelectionModel) e.getSource(); - if (lsm.isSelectionEmpty()) { - //no rows are selected - } else { - StringBuffer buf = new StringBuffer(); - int selectedRow = lsm.getMinSelectionIndex(); - - for (int i = 0; i < _numCols - 1; i++) { - String value = ""; - Object obj = _table.getModel().getValueAt(selectedRow, i); - if (obj != null) { - value = obj.toString(); - } - - buf.append(_colNames[i] + ":"); - buf.append("\t"); - - if (i == _colThread || i == _colMessage || i == _colLevel) { - buf.append("\t"); // pad out the date. - } - - if (i == _colDate || i == _colNDC) { - buf.append("\t\t"); // pad out the date. - } - -// if( i == _colSequence) -// { -// buf.append("\t\t\t"); // pad out the Sequnce. -// } - - buf.append(value); - buf.append("\n"); - } - buf.append(_colNames[_numCols - 1] + ":\n"); - Object obj = _table.getModel().getValueAt(selectedRow, _numCols - 1); - if (obj != null) { - buf.append(obj.toString()); - } - - _detailTextArea.setText(buf.toString()); - } - } - } -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/LogTableColumn.java b/java/src/org/apache/log4j/lf5/viewer/LogTableColumn.java deleted file mode 100644 index c86c6bb..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LogTableColumn.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * LogTableColumn - * - * @author Michael J. Sikorsky - * @author Brad Marlborough - */ - -// Contributed by ThoughtWorks Inc. - -public class LogTableColumn implements java.io.Serializable { - private static final long serialVersionUID = -4275827753626456547L; - - // log4j table columns. - public final static LogTableColumn DATE = new LogTableColumn("Date"); - public final static LogTableColumn THREAD = new LogTableColumn("Thread"); - public final static LogTableColumn MESSAGE_NUM = new LogTableColumn("Message #"); - public final static LogTableColumn LEVEL = new LogTableColumn("Level"); - public final static LogTableColumn NDC = new LogTableColumn("NDC"); - public final static LogTableColumn CATEGORY = new LogTableColumn("Category"); - public final static LogTableColumn MESSAGE = new LogTableColumn("Message"); - public final static LogTableColumn LOCATION = new LogTableColumn("Location"); - public final static LogTableColumn THROWN = new LogTableColumn("Thrown"); - - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected String _label; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - private static LogTableColumn[] _log4JColumns; - private static Map _logTableColumnMap; - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - static { - _log4JColumns = new LogTableColumn[]{DATE, THREAD, MESSAGE_NUM, LEVEL, NDC, CATEGORY, - MESSAGE, LOCATION, THROWN}; - - _logTableColumnMap = new HashMap(); - - for (int i = 0; i < _log4JColumns.length; i++) { - _logTableColumnMap.put(_log4JColumns[i].getLabel(), _log4JColumns[i]); - } - } - - - public LogTableColumn(String label) { - _label = label; - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Return the Label of the LogLevel. - */ - public String getLabel() { - return _label; - } - - /** - * Convert a column label into a LogTableColumn object. - * - * @param column The label of a level to be converted into a LogTableColumn. - * @return LogTableColumn The LogTableColumn with a label equal to column. - * @throws LogTableColumnFormatException Is thrown when the column can not be - * converted into a LogTableColumn. - */ - public static LogTableColumn valueOf(String column) - throws LogTableColumnFormatException { - LogTableColumn tableColumn = null; - if (column != null) { - column = column.trim(); - tableColumn = (LogTableColumn) _logTableColumnMap.get(column); - } - - if (tableColumn == null) { - StringBuffer buf = new StringBuffer(); - buf.append("Error while trying to parse (" + column + ") into"); - buf.append(" a LogTableColumn."); - throw new LogTableColumnFormatException(buf.toString()); - } - return tableColumn; - } - - - public boolean equals(Object o) { - boolean equals = false; - - if (o instanceof LogTableColumn) { - if (this.getLabel() == - ((LogTableColumn) o).getLabel()) { - equals = true; - } - } - - return equals; - } - - public int hashCode() { - return _label.hashCode(); - } - - public String toString() { - return _label; - } - - /** - * @return A List of LogTableColumn/code> objects that map - * to log4j Column objects. - */ - public static List getLogTableColumns() { - return Arrays.asList(_log4JColumns); - } - - public static LogTableColumn[] getLogTableColumnArray() { - return _log4JColumns; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java b/java/src/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java deleted file mode 100644 index b161fe7..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -/** - * Thrown to indicate that the client has attempted to convert a string - * to one the LogLevel types, but the string does not have the appropriate - * format. - * - * @author Michael J. Sikorsky - */ - -// Contributed by ThoughtWorks Inc. - -public class LogTableColumnFormatException extends Exception { - private static final long serialVersionUID = 6529165785030431653L; - - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public LogTableColumnFormatException(String message) { - super(message); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/LogTableModel.java b/java/src/org/apache/log4j/lf5/viewer/LogTableModel.java deleted file mode 100644 index 3b60000..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LogTableModel.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import javax.swing.table.DefaultTableModel; - -/** - * LogTableModel - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class LogTableModel extends DefaultTableModel { - private static final long serialVersionUID = 3593300685868700894L; - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public LogTableModel(Object[] colNames, int numRows) { - super(colNames, numRows); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public boolean isCellEditable(int row, int column) { - return (false); - } - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java b/java/src/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java deleted file mode 100644 index 3d1e397..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import java.awt.Color; -import java.awt.Component; - -import javax.swing.JTable; -import javax.swing.table.DefaultTableCellRenderer; - -import org.apache.log4j.lf5.LogLevel; -import org.apache.log4j.lf5.LogRecord; - -/** - * LogTableRowRenderer - * - * @author Michael J. Sikorsky - * @author Robert Shaw - * @author Brad Marlborough - */ - -// Contributed by ThoughtWorks Inc. - -public class LogTableRowRenderer extends DefaultTableCellRenderer { - private static final long serialVersionUID = -3951639953706443213L; - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected boolean _highlightFatal = true; - protected Color _color = new Color(230, 230, 230); - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public Component getTableCellRendererComponent(JTable table, - Object value, - boolean isSelected, - boolean hasFocus, - int row, - int col) { - - if ((row % 2) == 0) { - setBackground(_color); - } else { - setBackground(Color.white); - } - - FilteredLogTableModel model = (FilteredLogTableModel) table.getModel(); - LogRecord record = model.getFilteredRecord(row); - - setForeground(getLogLevelColor(record.getLevel())); - - return (super.getTableCellRendererComponent(table, - value, - isSelected, - hasFocus, - row, col)); - } - - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - protected Color getLogLevelColor(LogLevel level) { - return (Color) LogLevel.getLogLevelColorMap().get(level); - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java b/java/src/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java deleted file mode 100644 index 4aa959c..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer; - -import java.awt.Adjustable; -import java.awt.event.AdjustmentEvent; -import java.awt.event.AdjustmentListener; - -/** - * An AdjustmentListener which ensures that an Adjustable (e.g. a Scrollbar) - * will "track" when the Adjustable expands. - * For example, when a vertical scroll bar is at its bottom anchor, - * the scrollbar will remain at the bottom. When the vertical scroll bar - * is at any other location, then no tracking will happen. - * An instance of this class should only listen to one Adjustable as - * it retains state information about the Adjustable it listens to. - * - * @author Richard Wan - */ - -// Contributed by ThoughtWorks Inc. - -public class TrackingAdjustmentListener implements AdjustmentListener { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - protected int _lastMaximum = -1; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public void adjustmentValueChanged(AdjustmentEvent e) { - Adjustable bar = e.getAdjustable(); - int currentMaximum = bar.getMaximum(); - if (bar.getMaximum() == _lastMaximum) { - return; // nothing to do, the adjustable has not expanded - } - int bottom = bar.getValue() + bar.getVisibleAmount(); - - if (bottom + bar.getUnitIncrement() >= _lastMaximum) { - bar.setValue(bar.getMaximum()); // use the most recent maximum - } - _lastMaximum = currentMaximum; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} - diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java deleted file mode 100644 index e5bbaa6..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import java.awt.Component; -import java.awt.event.MouseEvent; -import java.util.EventObject; - -import javax.swing.JTable; -import javax.swing.JTree; -import javax.swing.event.CellEditorListener; -import javax.swing.event.ChangeEvent; -import javax.swing.event.EventListenerList; -import javax.swing.table.TableCellEditor; -import javax.swing.tree.TreeCellEditor; - -/** - * CategoryAbstractCellEditor. Base class to handle the some common - * details of cell editing. - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryAbstractCellEditor implements TableCellEditor, TreeCellEditor { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected EventListenerList _listenerList = new EventListenerList(); - protected Object _value; - protected ChangeEvent _changeEvent = null; - protected int _clickCountToStart = 1; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public Object getCellEditorValue() { - return _value; - } - - public void setCellEditorValue(Object value) { - _value = value; - } - - public void setClickCountToStart(int count) { - _clickCountToStart = count; - } - - public int getClickCountToStart() { - return _clickCountToStart; - } - - public boolean isCellEditable(EventObject anEvent) { - if (anEvent instanceof MouseEvent) { - if (((MouseEvent) anEvent).getClickCount() < _clickCountToStart) { - return false; - } - } - return true; - } - - public boolean shouldSelectCell(EventObject anEvent) { - if (this.isCellEditable(anEvent)) { - if (anEvent == null || - ((MouseEvent) anEvent).getClickCount() >= _clickCountToStart) { - return true; - } - } - return false; - } - - public boolean stopCellEditing() { - fireEditingStopped(); - return true; - } - - public void cancelCellEditing() { - fireEditingCanceled(); - } - - public void addCellEditorListener(CellEditorListener l) { - _listenerList.add(CellEditorListener.class, l); - } - - public void removeCellEditorListener(CellEditorListener l) { - _listenerList.remove(CellEditorListener.class, l); - } - - public Component getTreeCellEditorComponent( - JTree tree, Object value, - boolean isSelected, - boolean expanded, - boolean leaf, int row) { - return null; - } - - public Component getTableCellEditorComponent( - JTable table, Object value, - boolean isSelected, - int row, int column) { - return null; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - protected void fireEditingStopped() { - Object[] listeners = _listenerList.getListenerList(); - - for (int i = listeners.length - 2; i >= 0; i -= 2) { - if (listeners[i] == CellEditorListener.class) { - if (_changeEvent == null) { - _changeEvent = new ChangeEvent(this); - } - - ((CellEditorListener) listeners[i + 1]).editingStopped(_changeEvent); - } - } - } - - protected void fireEditingCanceled() { - Object[] listeners = _listenerList.getListenerList(); - - for (int i = listeners.length - 2; i >= 0; i -= 2) { - if (listeners[i] == CellEditorListener.class) { - if (_changeEvent == null) { - _changeEvent = new ChangeEvent(this); - } - - ((CellEditorListener) listeners[i + 1]).editingCanceled(_changeEvent); - } - } - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java deleted file mode 100644 index 1391fd5..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -/** - * CategoryElement represents a single element or part of a Category. - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryElement { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected String _categoryTitle; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public CategoryElement() { - super(); - } - - public CategoryElement(String title) { - _categoryTitle = title; - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public String getTitle() { - return (_categoryTitle); - } - - public void setTitle(String title) { - _categoryTitle = title; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java deleted file mode 100644 index 63f91ae..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import org.apache.log4j.lf5.LogRecord; -import org.apache.log4j.lf5.LogRecordFilter; - -import java.util.Enumeration; - -/** - * An implementation of LogRecordFilter based on a CategoryExplorerModel - * - * @author Richard Wan - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryExplorerLogRecordFilter implements LogRecordFilter { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - protected CategoryExplorerModel _model; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public CategoryExplorerLogRecordFilter(CategoryExplorerModel model) { - _model = model; - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * @return true if the CategoryExplorer model specified at construction - * is accepting the category of the specified LogRecord. Has a side-effect - * of adding the CategoryPath of the LogRecord to the explorer model - * if the CategoryPath is new. - */ - public boolean passes(LogRecord record) { - CategoryPath path = new CategoryPath(record.getCategory()); - return _model.isCategoryPathActive(path); - } - - /** - * Resets the counters for the contained CategoryNodes to zero. - */ - public void reset() { - resetAllNodes(); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected void resetAllNodes() { - Enumeration nodes = _model.getRootCategoryNode().depthFirstEnumeration(); - CategoryNode current; - while (nodes.hasMoreElements()) { - current = (CategoryNode) nodes.nextElement(); - current.resetNumberOfContainedRecords(); - _model.nodeChanged(current); - } - } - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} - diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java deleted file mode 100644 index c70b55c..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import java.awt.AWTEventMulticaster; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Enumeration; - -import javax.swing.SwingUtilities; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; - -import org.apache.log4j.lf5.LogRecord; - -/** - * CategoryExplorerModel - * - * @author Michael J. Sikorsky - * @author Robert Shaw - * @author Brent Sprecher - * @author Richard Hurst - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryExplorerModel extends DefaultTreeModel { - private static final long serialVersionUID = -3413887384316015901L; - - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - protected boolean _renderFatal = true; - protected ActionListener _listener = null; - protected ActionEvent _event = new ActionEvent(this, - ActionEvent.ACTION_PERFORMED, - "Nodes Selection changed"); - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public CategoryExplorerModel(CategoryNode node) { - super(node); - } - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public void addLogRecord(LogRecord lr) { - CategoryPath path = new CategoryPath(lr.getCategory()); - addCategory(path); // create category path if it is new - CategoryNode node = getCategoryNode(path); - node.addRecord(); // update category node - if (_renderFatal && lr.isFatal()) { - TreeNode[] nodes = getPathToRoot(node); - int len = nodes.length; - CategoryNode parent; - - // i = 0 gives root node - // skip node and root, loop through "parents" in between - for (int i = 1; i < len - 1; i++) { - parent = (CategoryNode) nodes[i]; - parent.setHasFatalChildren(true); - nodeChanged(parent); - } - node.setHasFatalRecords(true); - nodeChanged(node); - } - } - - public CategoryNode getRootCategoryNode() { - return (CategoryNode) getRoot(); - } - - public CategoryNode getCategoryNode(String category) { - CategoryPath path = new CategoryPath(category); - return (getCategoryNode(path)); - } - - /** - * returns null if no CategoryNode exists. - */ - public CategoryNode getCategoryNode(CategoryPath path) { - CategoryNode root = (CategoryNode) getRoot(); - CategoryNode parent = root; // Start condition. - - for (int i = 0; i < path.size(); i++) { - CategoryElement element = path.categoryElementAt(i); - - // If the two nodes have matching titles they are considered equal. - Enumeration children = parent.children(); - - boolean categoryAlreadyExists = false; - while (children.hasMoreElements()) { - CategoryNode node = (CategoryNode) children.nextElement(); - String title = node.getTitle().toLowerCase(); - - String pathLC = element.getTitle().toLowerCase(); - if (title.equals(pathLC)) { - categoryAlreadyExists = true; - // This is now the new parent node. - parent = node; - break; // out of the while, and back to the for(). - } - } - - if (categoryAlreadyExists == false) { - return null; // Didn't find the Node. - } - } - - return (parent); - } - - /** - * @return true if all the nodes in the specified CategoryPath are - * selected. - */ - public boolean isCategoryPathActive(CategoryPath path) { - CategoryNode root = (CategoryNode) getRoot(); - CategoryNode parent = root; // Start condition. - boolean active = false; - - for (int i = 0; i < path.size(); i++) { - CategoryElement element = path.categoryElementAt(i); - - // If the two nodes have matching titles they are considered equal. - Enumeration children = parent.children(); - - boolean categoryAlreadyExists = false; - active = false; - - while (children.hasMoreElements()) { - CategoryNode node = (CategoryNode) children.nextElement(); - String title = node.getTitle().toLowerCase(); - - String pathLC = element.getTitle().toLowerCase(); - if (title.equals(pathLC)) { - categoryAlreadyExists = true; - // This is now the new parent node. - parent = node; - - if (parent.isSelected()) { - active = true; - } - - break; // out of the while, and back to the for(). - } - } - - if (active == false || categoryAlreadyExists == false) { - return false; - } - } - - return (active); - } - - - /** - *

Method altered by Richard Hurst such that it returns the CategoryNode - * corresponding to the CategoryPath

- * - * @param path category path. - * @return CategoryNode - */ - public CategoryNode addCategory(CategoryPath path) { - CategoryNode root = (CategoryNode) getRoot(); - CategoryNode parent = root; // Start condition. - - for (int i = 0; i < path.size(); i++) { - CategoryElement element = path.categoryElementAt(i); - - // If the two nodes have matching titles they are considered equal. - Enumeration children = parent.children(); - - boolean categoryAlreadyExists = false; - while (children.hasMoreElements()) { - CategoryNode node = (CategoryNode) children.nextElement(); - String title = node.getTitle().toLowerCase(); - - String pathLC = element.getTitle().toLowerCase(); - if (title.equals(pathLC)) { - categoryAlreadyExists = true; - // This is now the new parent node. - parent = node; - break; - } - } - - if (categoryAlreadyExists == false) { - // We need to add the node. - CategoryNode newNode = new CategoryNode(element.getTitle()); - - //This method of adding a new node cause parent roots to be - // collapsed. - //parent.add( newNode ); - //reload(parent); - - // This doesn't force the nodes to collapse. - insertNodeInto(newNode, parent, parent.getChildCount()); - refresh(newNode); - - // The newly added node is now the parent. - parent = newNode; - - } - } - - return parent; - } - - public void update(CategoryNode node, boolean selected) { - if (node.isSelected() == selected) { - return; // nothing was changed, nothing to do - } - // select parents or deselect children - if (selected) { - setParentSelection(node, true); - } else { - setDescendantSelection(node, false); - } - } - - public void setDescendantSelection(CategoryNode node, boolean selected) { - Enumeration descendants = node.depthFirstEnumeration(); - CategoryNode current; - while (descendants.hasMoreElements()) { - current = (CategoryNode) descendants.nextElement(); - // does the current node need to be changed? - if (current.isSelected() != selected) { - current.setSelected(selected); - nodeChanged(current); - } - } - notifyActionListeners(); - } - - public void setParentSelection(CategoryNode node, boolean selected) { - TreeNode[] nodes = getPathToRoot(node); - int len = nodes.length; - CategoryNode parent; - - // i = 0 gives root node, i=len-1 gives this node - // skip the root node - for (int i = 1; i < len; i++) { - parent = (CategoryNode) nodes[i]; - if (parent.isSelected() != selected) { - parent.setSelected(selected); - nodeChanged(parent); - } - } - notifyActionListeners(); - } - - - public synchronized void addActionListener(ActionListener l) { - _listener = AWTEventMulticaster.add(_listener, l); - } - - public synchronized void removeActionListener(ActionListener l) { - _listener = AWTEventMulticaster.remove(_listener, l); - } - - public void resetAllNodeCounts() { - Enumeration nodes = getRootCategoryNode().depthFirstEnumeration(); - CategoryNode current; - while (nodes.hasMoreElements()) { - current = (CategoryNode) nodes.nextElement(); - current.resetNumberOfContainedRecords(); - nodeChanged(current); - } - } - - /** - *

Returns the CategoryPath to the specified CategoryNode

- * - * @param node The target CategoryNode - * @return CategoryPath - */ - public TreePath getTreePathToRoot(CategoryNode node) { - if (node == null) { - return null; - } - return (new TreePath(getPathToRoot(node))); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - protected void notifyActionListeners() { - if (_listener != null) { - _listener.actionPerformed(_event); - } - } - - /** - * Fires a nodechanged event on the SwingThread. - */ - protected void refresh(final CategoryNode node) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - nodeChanged(node); // remind the tree to render the new node - } - }); - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java deleted file mode 100644 index a8e97f5..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import java.awt.event.MouseEvent; - -import javax.swing.JTree; -import javax.swing.event.TreeModelEvent; -import javax.swing.tree.TreePath; - -/** - * CategoryExplorerTree - * - * @author Michael J. Sikorsky - * @author Robert Shaw - * @author Brent Sprecher - * @author Brad Marlborough - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryExplorerTree extends JTree { - private static final long serialVersionUID = 8066257446951323576L; - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected CategoryExplorerModel _model; - protected boolean _rootAlreadyExpanded = false; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - /** - * Construct a CategoryExplorerTree with a specific model. - */ - public CategoryExplorerTree(CategoryExplorerModel model) { - super(model); - - _model = model; - init(); - } - - /** - * Construct a CategoryExplorerTree and create a default CategoryExplorerModel. - */ - public CategoryExplorerTree() { - super(); - - CategoryNode rootNode = new CategoryNode("Categories"); - - _model = new CategoryExplorerModel(rootNode); - - setModel(_model); - - init(); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public CategoryExplorerModel getExplorerModel() { - return (_model); - } - - public String getToolTipText(MouseEvent e) { - - try { - return super.getToolTipText(e); - } catch (Exception ex) { - return ""; - } - - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected void init() { - // Put visible lines on the JTree. - putClientProperty("JTree.lineStyle", "Angled"); - - // Configure the Tree with the appropriate Renderers and Editors. - - CategoryNodeRenderer renderer = new CategoryNodeRenderer(); - setEditable(true); - setCellRenderer(renderer); - - CategoryNodeEditor editor = new CategoryNodeEditor(_model); - - setCellEditor(new CategoryImmediateEditor(this, - new CategoryNodeRenderer(), - editor)); - setShowsRootHandles(true); - - setToolTipText(""); - - ensureRootExpansion(); - - } - - protected void expandRootNode() { - if (_rootAlreadyExpanded) { - return; - } - _rootAlreadyExpanded = true; - TreePath path = new TreePath(_model.getRootCategoryNode().getPath()); - expandPath(path); - } - - protected void ensureRootExpansion() { - _model.addTreeModelListener(new TreeModelAdapter() { - public void treeNodesInserted(TreeModelEvent e) { - expandRootNode(); - } - }); - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java deleted file mode 100644 index 4c7374b..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import java.awt.Dimension; -import java.awt.Rectangle; -import java.awt.event.MouseEvent; -import java.util.EventObject; - -import javax.swing.Icon; -import javax.swing.JTree; -import javax.swing.tree.DefaultTreeCellEditor; -import javax.swing.tree.TreePath; - -/** - * CategoryImmediateEditor - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryImmediateEditor extends DefaultTreeCellEditor { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - private CategoryNodeRenderer renderer; - protected Icon editingIcon = null; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - public CategoryImmediateEditor(JTree tree, - CategoryNodeRenderer renderer, - CategoryNodeEditor editor) { - super(tree, renderer, editor); - this.renderer = renderer; - renderer.setIcon(null); - renderer.setLeafIcon(null); - renderer.setOpenIcon(null); - renderer.setClosedIcon(null); - - super.editingIcon = null; - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - public boolean shouldSelectCell(EventObject e) { - boolean rv = false; // only mouse events - - if (e instanceof MouseEvent) { - MouseEvent me = (MouseEvent) e; - TreePath path = tree.getPathForLocation(me.getX(), - me.getY()); - CategoryNode node = (CategoryNode) - path.getLastPathComponent(); - - rv = node.isLeaf() /*|| !inCheckBoxHitRegion(me)*/; - } - return rv; - } - - public boolean inCheckBoxHitRegion(MouseEvent e) { - TreePath path = tree.getPathForLocation(e.getX(), - e.getY()); - if (path == null) { - return false; - } - CategoryNode node = (CategoryNode) path.getLastPathComponent(); - boolean rv = false; - - if (true) { - // offset and lastRow DefaultTreeCellEditor - // protected members - - Rectangle bounds = tree.getRowBounds(lastRow); - Dimension checkBoxOffset = - renderer.getCheckBoxOffset(); - - bounds.translate(offset + checkBoxOffset.width, - checkBoxOffset.height); - - rv = bounds.contains(e.getPoint()); - } - return true; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected boolean canEditImmediately(EventObject e) { - boolean rv = false; - - if (e instanceof MouseEvent) { - MouseEvent me = (MouseEvent) e; - rv = inCheckBoxHitRegion(me); - } - - return rv; - } - - protected void determineOffset(JTree tree, Object value, - boolean isSelected, boolean expanded, - boolean leaf, int row) { - // Very important: means that the tree won't jump around. - offset = 0; - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java deleted file mode 100644 index 95bdc42..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeNode; -import java.util.Enumeration; - -/** - * CategoryNode - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryNode extends DefaultMutableTreeNode { - private static final long serialVersionUID = 5958994817693177319L; - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected boolean _selected = true; - protected int _numberOfContainedRecords = 0; - protected int _numberOfRecordsFromChildren = 0; - protected boolean _hasFatalChildren = false; - protected boolean _hasFatalRecords = false; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - /** - * - */ - public CategoryNode(String title) { - setUserObject(title); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - public String getTitle() { - return (String) getUserObject(); - } - - public void setSelected(boolean s) { - if (s != _selected) { - _selected = s; - } - } - - public boolean isSelected() { - return _selected; - } - - /** - * @deprecated - */ - public void setAllDescendantsSelected() { - Enumeration children = children(); - while (children.hasMoreElements()) { - CategoryNode node = (CategoryNode) children.nextElement(); - node.setSelected(true); - node.setAllDescendantsSelected(); - } - } - - /** - * @deprecated - */ - public void setAllDescendantsDeSelected() { - Enumeration children = children(); - while (children.hasMoreElements()) { - CategoryNode node = (CategoryNode) children.nextElement(); - node.setSelected(false); - node.setAllDescendantsDeSelected(); - } - } - - public String toString() { - return (getTitle()); - } - - public boolean equals(Object obj) { - if (obj instanceof CategoryNode) { - CategoryNode node = (CategoryNode) obj; - String tit1 = getTitle().toLowerCase(); - String tit2 = node.getTitle().toLowerCase(); - - if (tit1.equals(tit2)) { - return (true); - } - } - return (false); - } - - public int hashCode() { - return (getTitle().hashCode()); - } - - public void addRecord() { - _numberOfContainedRecords++; - addRecordToParent(); - } - - public int getNumberOfContainedRecords() { - return _numberOfContainedRecords; - } - - public void resetNumberOfContainedRecords() { - _numberOfContainedRecords = 0; - _numberOfRecordsFromChildren = 0; - _hasFatalRecords = false; - _hasFatalChildren = false; - } - - public boolean hasFatalRecords() { - return _hasFatalRecords; - } - - public boolean hasFatalChildren() { - return _hasFatalChildren; - } - - public void setHasFatalRecords(boolean flag) { - _hasFatalRecords = flag; - } - - public void setHasFatalChildren(boolean flag) { - _hasFatalChildren = flag; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected int getTotalNumberOfRecords() { - return getNumberOfRecordsFromChildren() + getNumberOfContainedRecords(); - } - - /** - * Passes up the addition from child to parent - */ - protected void addRecordFromChild() { - _numberOfRecordsFromChildren++; - addRecordToParent(); - } - - protected int getNumberOfRecordsFromChildren() { - return _numberOfRecordsFromChildren; - } - - protected void addRecordToParent() { - TreeNode parent = getParent(); - if (parent == null) { - return; - } - ((CategoryNode) parent).addRecordFromChild(); - } - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java deleted file mode 100644 index b653cd7..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.Enumeration; - -import javax.swing.JCheckBox; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPopupMenu; -import javax.swing.JTree; -import javax.swing.tree.TreePath; - -/** - * CategoryNodeEditor - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryNodeEditor extends CategoryAbstractCellEditor { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected CategoryNodeEditorRenderer _renderer; - protected CategoryNode _lastEditedNode; - protected JCheckBox _checkBox; - protected CategoryExplorerModel _categoryModel; - protected JTree _tree; - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public CategoryNodeEditor(CategoryExplorerModel model) { - _renderer = new CategoryNodeEditorRenderer(); - _checkBox = _renderer.getCheckBox(); - _categoryModel = model; - - _checkBox.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - _categoryModel.update(_lastEditedNode, _checkBox.isSelected()); - stopCellEditing(); - } - }); - - _renderer.addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e) { - if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) { - showPopup(_lastEditedNode, e.getX(), e.getY()); - } - stopCellEditing(); - } - }); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public Component getTreeCellEditorComponent(JTree tree, Object value, - boolean selected, boolean expanded, - boolean leaf, int row) { - _lastEditedNode = (CategoryNode) value; - _tree = tree; - - return _renderer.getTreeCellRendererComponent(tree, - value, selected, expanded, - leaf, row, true); - // hasFocus ignored - } - - public Object getCellEditorValue() { - return _lastEditedNode.getUserObject(); - } - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected JMenuItem createPropertiesMenuItem(final CategoryNode node) { - JMenuItem result = new JMenuItem("Properties"); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - showPropertiesDialog(node); - } - }); - return result; - } - - protected void showPropertiesDialog(CategoryNode node) { - JOptionPane.showMessageDialog( - _tree, - getDisplayedProperties(node), - "Category Properties: " + node.getTitle(), - JOptionPane.PLAIN_MESSAGE - ); - } - - protected Object getDisplayedProperties(CategoryNode node) { - ArrayList result = new ArrayList(); - result.add("Category: " + node.getTitle()); - if (node.hasFatalRecords()) { - result.add("Contains at least one fatal LogRecord."); - } - if (node.hasFatalChildren()) { - result.add("Contains descendants with a fatal LogRecord."); - } - result.add("LogRecords in this category alone: " + - node.getNumberOfContainedRecords()); - result.add("LogRecords in descendant categories: " + - node.getNumberOfRecordsFromChildren()); - result.add("LogRecords in this category including descendants: " + - node.getTotalNumberOfRecords()); - return result.toArray(); - } - - protected void showPopup(CategoryNode node, int x, int y) { - JPopupMenu popup = new JPopupMenu(); - popup.setSize(150, 400); - // - // Configure the Popup - // - if (node.getParent() == null) { - popup.add(createRemoveMenuItem()); - popup.addSeparator(); - } - popup.add(createSelectDescendantsMenuItem(node)); - popup.add(createUnselectDescendantsMenuItem(node)); - popup.addSeparator(); - popup.add(createExpandMenuItem(node)); - popup.add(createCollapseMenuItem(node)); - popup.addSeparator(); - popup.add(createPropertiesMenuItem(node)); - popup.show(_renderer, x, y); - } - - protected JMenuItem createSelectDescendantsMenuItem(final CategoryNode node) { - JMenuItem selectDescendants = - new JMenuItem("Select All Descendant Categories"); - selectDescendants.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - _categoryModel.setDescendantSelection(node, true); - } - } - ); - return selectDescendants; - } - - protected JMenuItem createUnselectDescendantsMenuItem(final CategoryNode node) { - JMenuItem unselectDescendants = - new JMenuItem("Deselect All Descendant Categories"); - unselectDescendants.addActionListener( - - new ActionListener() { - public void actionPerformed(ActionEvent e) { - _categoryModel.setDescendantSelection(node, false); - } - } - - ); - return unselectDescendants; - } - - protected JMenuItem createExpandMenuItem(final CategoryNode node) { - JMenuItem result = new JMenuItem("Expand All Descendant Categories"); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - expandDescendants(node); - } - }); - return result; - } - - protected JMenuItem createCollapseMenuItem(final CategoryNode node) { - JMenuItem result = new JMenuItem("Collapse All Descendant Categories"); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - collapseDescendants(node); - } - }); - return result; - } - - /** - * This featured was moved from the LogBrokerMonitor class - * to the CategoryNodeExplorer so that the Category tree - * could be pruned from the Category Explorer popup menu. - * This menu option only appears when a user right clicks on - * the Category parent node. - * - * See removeUnusedNodes() - */ - protected JMenuItem createRemoveMenuItem() { - JMenuItem result = new JMenuItem("Remove All Empty Categories"); - result.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - while (removeUnusedNodes() > 0) ; - } - }); - return result; - } - - protected void expandDescendants(CategoryNode node) { - Enumeration descendants = node.depthFirstEnumeration(); - CategoryNode current; - while (descendants.hasMoreElements()) { - current = (CategoryNode) descendants.nextElement(); - expand(current); - } - } - - protected void collapseDescendants(CategoryNode node) { - Enumeration descendants = node.depthFirstEnumeration(); - CategoryNode current; - while (descendants.hasMoreElements()) { - current = (CategoryNode) descendants.nextElement(); - collapse(current); - } - } - - /** - * Removes any inactive nodes from the Category tree. - */ - protected int removeUnusedNodes() { - int count = 0; - CategoryNode root = _categoryModel.getRootCategoryNode(); - Enumeration enumeration = root.depthFirstEnumeration(); - while (enumeration.hasMoreElements()) { - CategoryNode node = (CategoryNode) enumeration.nextElement(); - if (node.isLeaf() && node.getNumberOfContainedRecords() == 0 - && node.getParent() != null) { - _categoryModel.removeNodeFromParent(node); - count++; - } - } - - return count; - } - - protected void expand(CategoryNode node) { - _tree.expandPath(getTreePath(node)); - } - - protected TreePath getTreePath(CategoryNode node) { - return new TreePath(node.getPath()); - } - - protected void collapse(CategoryNode node) { - _tree.collapsePath(getTreePath(node)); - } - - //----------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java deleted file mode 100644 index 57be9fc..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import java.awt.Component; - -import javax.swing.JCheckBox; -import javax.swing.JTree; - -/** - * CategoryNodeEditorRenderer - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryNodeEditorRenderer extends CategoryNodeRenderer { - private static final long serialVersionUID = -6094804684259929574L; - - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public Component getTreeCellRendererComponent( - JTree tree, Object value, - boolean selected, boolean expanded, - boolean leaf, int row, - boolean hasFocus) { - Component c = super.getTreeCellRendererComponent(tree, - value, selected, expanded, - leaf, row, hasFocus); - - return c; - } - - public JCheckBox getCheckBox() { - return _checkBox; - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java deleted file mode 100644 index 4eb461d..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import javax.swing.*; -import javax.swing.tree.DefaultTreeCellRenderer; -import java.awt.*; -import java.net.URL; - -/** - * CategoryNodeRenderer - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryNodeRenderer extends DefaultTreeCellRenderer { - private static final long serialVersionUID = -6046702673278595048L; - - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - public static final Color FATAL_CHILDREN = new Color(189, 113, 0); - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected JCheckBox _checkBox = new JCheckBox(); - protected JPanel _panel = new JPanel(); - protected static ImageIcon _sat = null; -// protected JLabel _label = new JLabel(); - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - public CategoryNodeRenderer() { - _panel.setBackground(UIManager.getColor("Tree.textBackground")); - - if (_sat == null) { - // Load the satellite image. - String resource = - "/org/apache/log4j/lf5/viewer/images/channelexplorer_satellite.gif"; - URL satURL = getClass().getResource(resource); - - _sat = new ImageIcon(satURL); - } - - setOpaque(false); - _checkBox.setOpaque(false); - _panel.setOpaque(false); - - // The flowlayout set to LEFT is very important so that the editor - // doesn't jump around. - _panel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); - _panel.add(_checkBox); - _panel.add(this); - - setOpenIcon(_sat); - setClosedIcon(_sat); - setLeafIcon(_sat); - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - public Component getTreeCellRendererComponent( - JTree tree, Object value, - boolean selected, boolean expanded, - boolean leaf, int row, - boolean hasFocus) { - - CategoryNode node = (CategoryNode) value; - //FileNode node = (FileNode)value; - //String s = tree.convertValueToText(value, selected, - // expanded, leaf, row, hasFocus); - - super.getTreeCellRendererComponent( - tree, value, selected, expanded, - leaf, row, hasFocus); - - if (row == 0) { - // Root row -- no check box - _checkBox.setVisible(false); - } else { - _checkBox.setVisible(true); - _checkBox.setSelected(node.isSelected()); - } - String toolTip = buildToolTip(node); - _panel.setToolTipText(toolTip); - if (node.hasFatalChildren()) { - this.setForeground(FATAL_CHILDREN); - } - if (node.hasFatalRecords()) { - this.setForeground(Color.red); - } - - return _panel; - } - - public Dimension getCheckBoxOffset() { - return new Dimension(0, 0); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - protected String buildToolTip(CategoryNode node) { - StringBuffer result = new StringBuffer(); - result.append(node.getTitle()).append(" contains a total of "); - result.append(node.getTotalNumberOfRecords()); - result.append(" LogRecords."); - result.append(" Right-click for more info."); - return result.toString(); - } - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java deleted file mode 100644 index 8cc3da1..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import java.util.LinkedList; -import java.util.StringTokenizer; - -/** - * CategoryPath is a collection of CategoryItems which represent a - * path of categories. - * - * @author Michael J. Sikorsky - * @author Robert Shaw - */ - -// Contributed by ThoughtWorks Inc. - -public class CategoryPath { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - protected LinkedList _categoryElements = new LinkedList(); - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - public CategoryPath() { - super(); - } - - /** - * Construct a CategoryPath. If the category is null, it defaults to "Debug". - */ - public CategoryPath(String category) { - String processedCategory = category; - - if (processedCategory == null) { - processedCategory = "Debug"; - } - - processedCategory = processedCategory.replace('/', '.'); - processedCategory = processedCategory.replace('\\', '.'); - - StringTokenizer st = new StringTokenizer(processedCategory, "."); - while (st.hasMoreTokens()) { - String element = st.nextToken(); - addCategoryElement(new CategoryElement(element)); - } - } - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * returns the number of CategoryElements. - */ - public int size() { - int count = _categoryElements.size(); - - return (count); - } - - public boolean isEmpty() { - boolean empty = false; - - if (_categoryElements.size() == 0) { - empty = true; - } - - return (empty); - } - - - /** - * Removes all categoryElements. - */ - public void removeAllCategoryElements() { - _categoryElements.clear(); - } - - /** - * Adds the specified categoryElement to the end of the categoryElement set. - */ - public void addCategoryElement(CategoryElement categoryElement) { - _categoryElements.addLast(categoryElement); - } - - /** - * Returns the CategoryElement at the specified index. - */ - public CategoryElement categoryElementAt(int index) { - return ((CategoryElement) _categoryElements.get(index)); - } - - - public String toString() { - StringBuffer out = new StringBuffer(100); - - out.append("\n"); - out.append("===========================\n"); - out.append("CategoryPath: \n"); - out.append("---------------------------\n"); - - out.append("\nCategoryPath:\n\t"); - - if (this.size() > 0) { - for (int i = 0; i < this.size(); i++) { - out.append(this.categoryElementAt(i).toString()); - out.append("\n\t"); - } - } else { - out.append("<>"); - } - - out.append("\n"); - out.append("===========================\n"); - - return (out.toString()); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java deleted file mode 100644 index 7323dcc..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.lf5.viewer.categoryexplorer; - -import javax.swing.event.TreeModelEvent; -import javax.swing.event.TreeModelListener; - -/** - * Default implementation of TreeModelListener which does nothing. - * - * @author Richard Wan - */ - -// Contributed by ThoughtWorks Inc. - -public class TreeModelAdapter implements TreeModelListener { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - public void treeNodesChanged(TreeModelEvent e) { - } - - public void treeNodesInserted(TreeModelEvent e) { - } - - public void treeNodesRemoved(TreeModelEvent e) { - } - - public void treeStructureChanged(TreeModelEvent e) { - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} - diff --git a/java/src/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java b/java/src/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java deleted file mode 100644 index a94ffab..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.configure; - -import java.awt.Color; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import javax.swing.JCheckBoxMenuItem; -import javax.swing.tree.TreePath; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.apache.log4j.lf5.LogLevel; -import org.apache.log4j.lf5.LogLevelFormatException; -import org.apache.log4j.lf5.viewer.LogBrokerMonitor; -import org.apache.log4j.lf5.viewer.LogTable; -import org.apache.log4j.lf5.viewer.LogTableColumn; -import org.apache.log4j.lf5.viewer.LogTableColumnFormatException; -import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerModel; -import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerTree; -import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryNode; -import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryPath; -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -/** - *

ConfigurationManager handles the storage and retrival of the state of - * the CategoryExplorer - * - * @author Richard Hurst - * @author Brad Marlborough - */ - -// Contributed by ThoughtWorks Inc. - -public class ConfigurationManager extends Object { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - private static final String CONFIG_FILE_NAME = "lf5_configuration.xml"; - private static final String NAME = "name"; - private static final String PATH = "path"; - private static final String SELECTED = "selected"; - private static final String EXPANDED = "expanded"; - private static final String CATEGORY = "category"; - private static final String FIRST_CATEGORY_NAME = "Categories"; - private static final String LEVEL = "level"; - private static final String COLORLEVEL = "colorlevel"; - private static final String RED = "red"; - private static final String GREEN = "green"; - private static final String BLUE = "blue"; - private static final String COLUMN = "column"; - private static final String NDCTEXTFILTER = "searchtext"; - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - private LogBrokerMonitor _monitor = null; - private LogTable _table = null; - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - public ConfigurationManager(LogBrokerMonitor monitor, LogTable table) { - super(); - _monitor = monitor; - _table = table; - load(); - } - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - public void save() { - CategoryExplorerModel model = _monitor.getCategoryExplorerTree().getExplorerModel(); - CategoryNode root = model.getRootCategoryNode(); - - StringBuffer xml = new StringBuffer(2048); - openXMLDocument(xml); - openConfigurationXML(xml); - processLogRecordFilter(_monitor.getNDCTextFilter(), xml); - processLogLevels(_monitor.getLogLevelMenuItems(), xml); - processLogLevelColors(_monitor.getLogLevelMenuItems(), - LogLevel.getLogLevelColorMap(), xml); - processLogTableColumns(LogTableColumn.getLogTableColumns(), xml); - processConfigurationNode(root, xml); - closeConfigurationXML(xml); - store(xml.toString()); - } - - public void reset() { - deleteConfigurationFile(); - collapseTree(); - selectAllNodes(); - } - - public static String treePathToString(TreePath path) { - // count begins at one so as to not include the 'Categories' - root category - StringBuffer sb = new StringBuffer(); - CategoryNode n = null; - Object[] objects = path.getPath(); - for (int i = 1; i < objects.length; i++) { - n = (CategoryNode) objects[i]; - if (i > 1) { - sb.append("."); - } - sb.append(n.getTitle()); - } - return sb.toString(); - } - - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - protected void load() { - File file = new File(getFilename()); - if (file.exists()) { - try { - DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory. - newInstance(); - DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); - Document doc = docBuilder.parse(file); - processRecordFilter(doc); - processCategories(doc); - processLogLevels(doc); - processLogLevelColors(doc); - processLogTableColumns(doc); - } catch (Exception e) { - // ignore all error and just continue as if there was no - // configuration xml file but do report a message - System.err.println("Unable process configuration file at " + - getFilename() + ". Error Message=" + e.getMessage()); - } - } - - } - - // Added in version 1.2 - reads in the NDC text filter from the - // xml configuration file. If the value of the filter is not null - // or an empty string ("") then the manager will set the LogBrokerMonitor's - // LogRecordFilter to use the NDC LogRecordFilter. Otherwise, the - // LogBrokerMonitor will use the default LogRecordFilter. - protected void processRecordFilter(Document doc) { - NodeList nodeList = doc.getElementsByTagName(NDCTEXTFILTER); - - // there is only one value stored - Node n = nodeList.item(0); - // add check for backwards compatibility as this feature was added in - // version 1.2 - if (n == null) { - return; - } - - NamedNodeMap map = n.getAttributes(); - String text = getValue(map, NAME); - - if (text == null || text.equals("")) { - return; - } - _monitor.setNDCLogRecordFilter(text); - } - - protected void processCategories(Document doc) { - CategoryExplorerTree tree = _monitor.getCategoryExplorerTree(); - CategoryExplorerModel model = tree.getExplorerModel(); - NodeList nodeList = doc.getElementsByTagName(CATEGORY); - - // determine where the starting node is - NamedNodeMap map = nodeList.item(0).getAttributes(); - int j = (getValue(map, NAME).equalsIgnoreCase(FIRST_CATEGORY_NAME)) ? 1 : 0; - // iterate backwards throught the nodeList so that expansion of the - // list can occur - for (int i = nodeList.getLength() - 1; i >= j; i--) { - Node n = nodeList.item(i); - map = n.getAttributes(); - CategoryNode chnode = model.addCategory(new CategoryPath(getValue(map, PATH))); - chnode.setSelected((getValue(map, SELECTED).equalsIgnoreCase("true")) ? true : false); - if (getValue(map, EXPANDED).equalsIgnoreCase("true")) ; - tree.expandPath(model.getTreePathToRoot(chnode)); - } - - } - - protected void processLogLevels(Document doc) { - NodeList nodeList = doc.getElementsByTagName(LEVEL); - Map menuItems = _monitor.getLogLevelMenuItems(); - - for (int i = 0; i < nodeList.getLength(); i++) { - Node n = nodeList.item(i); - NamedNodeMap map = n.getAttributes(); - String name = getValue(map, NAME); - try { - JCheckBoxMenuItem item = - (JCheckBoxMenuItem) menuItems.get(LogLevel.valueOf(name)); - item.setSelected(getValue(map, SELECTED).equalsIgnoreCase("true")); - } catch (LogLevelFormatException e) { - // ignore it will be on by default. - } - } - } - - protected void processLogLevelColors(Document doc) { - NodeList nodeList = doc.getElementsByTagName(COLORLEVEL); - LogLevel.getLogLevelColorMap(); - - for (int i = 0; i < nodeList.getLength(); i++) { - Node n = nodeList.item(i); - // check for backwards compatibility since this feature was added - // in version 1.3 - if (n == null) { - return; - } - - NamedNodeMap map = n.getAttributes(); - String name = getValue(map, NAME); - try { - LogLevel level = LogLevel.valueOf(name); - int red = Integer.parseInt(getValue(map, RED)); - int green = Integer.parseInt(getValue(map, GREEN)); - int blue = Integer.parseInt(getValue(map, BLUE)); - Color c = new Color(red, green, blue); - if (level != null) { - level.setLogLevelColorMap(level, c); - } - - } catch (LogLevelFormatException e) { - // ignore it will be on by default. - } - } - } - - protected void processLogTableColumns(Document doc) { - NodeList nodeList = doc.getElementsByTagName(COLUMN); - Map menuItems = _monitor.getLogTableColumnMenuItems(); - List selectedColumns = new ArrayList(); - for (int i = 0; i < nodeList.getLength(); i++) { - Node n = nodeList.item(i); - // check for backwards compatibility since this feature was added - // in version 1.3 - if (n == null) { - return; - } - NamedNodeMap map = n.getAttributes(); - String name = getValue(map, NAME); - try { - LogTableColumn column = LogTableColumn.valueOf(name); - JCheckBoxMenuItem item = - (JCheckBoxMenuItem) menuItems.get(column); - item.setSelected(getValue(map, SELECTED).equalsIgnoreCase("true")); - - if (item.isSelected()) { - selectedColumns.add(column); - } - } catch (LogTableColumnFormatException e) { - // ignore it will be on by default. - } - - if (selectedColumns.isEmpty()) { - _table.setDetailedView(); - } else { - _table.setView(selectedColumns); - } - - } - } - - protected String getValue(NamedNodeMap map, String attr) { - Node n = map.getNamedItem(attr); - return n.getNodeValue(); - } - - protected void collapseTree() { - // collapse everything except the first category - CategoryExplorerTree tree = _monitor.getCategoryExplorerTree(); - for (int i = tree.getRowCount() - 1; i > 0; i--) { - tree.collapseRow(i); - } - } - - protected void selectAllNodes() { - CategoryExplorerModel model = _monitor.getCategoryExplorerTree().getExplorerModel(); - CategoryNode root = model.getRootCategoryNode(); - Enumeration all = root.breadthFirstEnumeration(); - CategoryNode n = null; - while (all.hasMoreElements()) { - n = (CategoryNode) all.nextElement(); - n.setSelected(true); - } - } - - protected void store(String s) { - - try { - PrintWriter writer = new PrintWriter(new FileWriter(getFilename())); - writer.print(s); - writer.close(); - } catch (IOException e) { - // do something with this error. - e.printStackTrace(); - } - - } - - protected void deleteConfigurationFile() { - try { - File f = new File(getFilename()); - if (f.exists()) { - f.delete(); - } - } catch (SecurityException e) { - System.err.println("Cannot delete " + getFilename() + - " because a security violation occured."); - } - } - - protected String getFilename() { - String home = System.getProperty("user.home"); - String sep = System.getProperty("file.separator"); - - return home + sep + "lf5" + sep + CONFIG_FILE_NAME; - } - - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - private void processConfigurationNode(CategoryNode node, StringBuffer xml) { - CategoryExplorerModel model = _monitor.getCategoryExplorerTree().getExplorerModel(); - - Enumeration all = node.breadthFirstEnumeration(); - CategoryNode n = null; - while (all.hasMoreElements()) { - n = (CategoryNode) all.nextElement(); - exportXMLElement(n, model.getTreePathToRoot(n), xml); - } - - } - - private void processLogLevels(Map logLevelMenuItems, StringBuffer xml) { - xml.append("\t\r\n"); - Iterator it = logLevelMenuItems.keySet().iterator(); - while (it.hasNext()) { - LogLevel level = (LogLevel) it.next(); - JCheckBoxMenuItem item = (JCheckBoxMenuItem) logLevelMenuItems.get(level); - exportLogLevelXMLElement(level.getLabel(), item.isSelected(), xml); - } - - xml.append("\t\r\n"); - } - - private void processLogLevelColors(Map logLevelMenuItems, Map logLevelColors, StringBuffer xml) { - xml.append("\t\r\n"); - // iterate through the list of log levels being used (log4j, jdk1.4, custom levels) - Iterator it = logLevelMenuItems.keySet().iterator(); - while (it.hasNext()) { - LogLevel level = (LogLevel) it.next(); - // for each level, get the associated color from the log level color map - Color color = (Color) logLevelColors.get(level); - exportLogLevelColorXMLElement(level.getLabel(), color, xml); - } - - xml.append("\t\r\n"); - } - - - private void processLogTableColumns(List logTableColumnMenuItems, StringBuffer xml) { - xml.append("\t\r\n"); - Iterator it = logTableColumnMenuItems.iterator(); - while (it.hasNext()) { - LogTableColumn column = (LogTableColumn) it.next(); - JCheckBoxMenuItem item = _monitor.getTableColumnMenuItem(column); - exportLogTableColumnXMLElement(column.getLabel(), item.isSelected(), xml); - } - - xml.append("\t\r\n"); - } - - // Added in version 1.2 - stores the NDC text filter in the xml file - // for future use. - private void processLogRecordFilter(String text, StringBuffer xml) { - xml.append("\t<").append(NDCTEXTFILTER).append(" "); - xml.append(NAME).append("=\"").append(text).append("\""); - xml.append("/>\r\n"); - } - - private void openXMLDocument(StringBuffer xml) { - xml.append("\r\n"); - } - - private void openConfigurationXML(StringBuffer xml) { - xml.append("\r\n"); - } - - private void closeConfigurationXML(StringBuffer xml) { - xml.append("\r\n"); - } - - private void exportXMLElement(CategoryNode node, TreePath path, StringBuffer xml) { - CategoryExplorerTree tree = _monitor.getCategoryExplorerTree(); - - xml.append("\t<").append(CATEGORY).append(" "); - xml.append(NAME).append("=\"").append(node.getTitle()).append("\" "); - xml.append(PATH).append("=\"").append(treePathToString(path)).append("\" "); - xml.append(EXPANDED).append("=\"").append(tree.isExpanded(path)).append("\" "); - xml.append(SELECTED).append("=\"").append(node.isSelected()).append("\"/>\r\n"); - } - - private void exportLogLevelXMLElement(String label, boolean selected, StringBuffer xml) { - xml.append("\t\t<").append(LEVEL).append(" ").append(NAME); - xml.append("=\"").append(label).append("\" "); - xml.append(SELECTED).append("=\"").append(selected); - xml.append("\"/>\r\n"); - } - - private void exportLogLevelColorXMLElement(String label, Color color, StringBuffer xml) { - xml.append("\t\t<").append(COLORLEVEL).append(" ").append(NAME); - xml.append("=\"").append(label).append("\" "); - xml.append(RED).append("=\"").append(color.getRed()).append("\" "); - xml.append(GREEN).append("=\"").append(color.getGreen()).append("\" "); - xml.append(BLUE).append("=\"").append(color.getBlue()); - xml.append("\"/>\r\n"); - } - - private void exportLogTableColumnXMLElement(String label, boolean selected, StringBuffer xml) { - xml.append("\t\t<").append(COLUMN).append(" ").append(NAME); - xml.append("=\"").append(label).append("\" "); - xml.append(SELECTED).append("=\"").append(selected); - xml.append("\"/>\r\n"); - } - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces: - //-------------------------------------------------------------------------- - -} - - - - - - diff --git a/java/src/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java b/java/src/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java deleted file mode 100644 index 6ff275d..0000000 --- a/java/src/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.lf5.viewer.configure; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.net.URL; -import java.util.Iterator; -import java.util.LinkedList; - - -/** - *

MRUFileManager handles the storage and retrival the most - * recently opened log files. - * - * @author Brad Marlborough - * @author Richard Hurst - */ - -// Contributed by ThoughtWorks Inc. - -public class MRUFileManager { - //-------------------------------------------------------------------------- - // Constants: - //-------------------------------------------------------------------------- - private static final String CONFIG_FILE_NAME = "mru_file_manager"; - private static final int DEFAULT_MAX_SIZE = 3; - - //-------------------------------------------------------------------------- - // Protected Variables: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Private Variables: - //-------------------------------------------------------------------------- - private int _maxSize = 0; - private LinkedList _mruFileList; - - //-------------------------------------------------------------------------- - // Constructors: - //-------------------------------------------------------------------------- - public MRUFileManager() { - load(); - setMaxSize(DEFAULT_MAX_SIZE); - } - - public MRUFileManager(int maxSize) { - load(); - setMaxSize(maxSize); - } - //-------------------------------------------------------------------------- - // Public Methods: - //-------------------------------------------------------------------------- - - /** - * Saves a list of MRU files out to a file. - */ - public void save() { - File file = new File(getFilename()); - - try { - ObjectOutputStream oos = new ObjectOutputStream(new - FileOutputStream(file)); - oos.writeObject(_mruFileList); - oos.flush(); - oos.close(); - } catch (Exception e) { - // do nothing - e.printStackTrace(); - } - } - - /** - * Gets the size of the MRU file list. - */ - public int size() { - return _mruFileList.size(); - } - - /** - * Returns a particular file name stored in a MRU file - * list based on an index value. - */ - public Object getFile(int index) { - if (index < size()) { - return _mruFileList.get(index); - } - - return null; - } - - /** - * Returns a input stream to the resource at the specified index - */ - public InputStream getInputStream(int index) throws IOException, - FileNotFoundException { - if (index < size()) { - Object o = getFile(index); - if (o instanceof File) { - return getInputStream((File) o); - } else { - return getInputStream((URL) o); - } - } - return null; - } - - /** - * Adds a file name to the MRU file list. - */ - public void set(File file) { - setMRU(file); - } - - /** - * Adds a url to the MRU file list. - */ - public void set(URL url) { - setMRU(url); - } - - /** - * Gets the list of files stored in the MRU file list. - */ - public String[] getMRUFileList() { - if (size() == 0) { - return null; - } - - String[] ss = new String[size()]; - - for (int i = 0; i < size(); i++) { - Object o = getFile(i); - if (o instanceof File) { - ss[i] = ((File) o).getAbsolutePath(); - } else // must be a url - { - ss[i] = o.toString(); - } - - } - - return ss; - } - - /** - * Moves the the index to the top of the MRU List - * - * @param index The index to be first in the mru list - */ - public void moveToTop(int index) { - _mruFileList.add(0, _mruFileList.remove(index)); - } - - /** - * Creates the directory where the MRU file list will be written. - * The "lf5" directory is created in the Documents and Settings - * directory on Windows 2000 machines and where ever the user.home - * variable points on all other platforms. - */ - public static void createConfigurationDirectory() { - String home = System.getProperty("user.home"); - String sep = System.getProperty("file.separator"); - File f = new File(home + sep + "lf5"); - if (!f.exists()) { - try { - f.mkdir(); - } catch (SecurityException e) { - e.printStackTrace(); - } - } - - } - //-------------------------------------------------------------------------- - // Protected Methods: - //-------------------------------------------------------------------------- - /** - * Gets an input stream for the corresponding file. - * - * @param file The file to create the input stream from. - * @return InputStream - */ - protected InputStream getInputStream(File file) throws IOException, - FileNotFoundException { - BufferedInputStream reader = - new BufferedInputStream(new FileInputStream(file)); - - return reader; - } - - /** - * Gets an input stream for the corresponding URL. - * - * @param url The url to create the input stream from. - * @return InputStream - */ - protected InputStream getInputStream(URL url) throws IOException { - return url.openStream(); - } - - /** - * Adds an object to the mru. - */ - protected void setMRU(Object o) { - int index = _mruFileList.indexOf(o); - - if (index == -1) { - _mruFileList.add(0, o); - setMaxSize(_maxSize); - } else { - moveToTop(index); - } - } - - /** - * Loads the MRU file list in from a file and stores it in a LinkedList. - * If no file exists, a new LinkedList is created. - */ - protected void load() { - createConfigurationDirectory(); - File file = new File(getFilename()); - if (file.exists()) { - try { - ObjectInputStream ois = new ObjectInputStream( - new FileInputStream(file)); - _mruFileList = (LinkedList) ois.readObject(); - ois.close(); - - // check that only files and url are in linked list - Iterator it = _mruFileList.iterator(); - while (it.hasNext()) { - Object o = it.next(); - if (!(o instanceof File) && !(o instanceof URL)) { - it.remove(); - } - } - } catch (Exception e) { - _mruFileList = new LinkedList(); - } - } else { - _mruFileList = new LinkedList(); - } - - } - - protected String getFilename() { - String home = System.getProperty("user.home"); - String sep = System.getProperty("file.separator"); - - return home + sep + "lf5" + sep + CONFIG_FILE_NAME; - } - - /** - * Ensures that the MRU list will have a MaxSize. - */ - protected void setMaxSize(int maxSize) { - if (maxSize < _mruFileList.size()) { - for (int i = 0; i < _mruFileList.size() - maxSize; i++) { - _mruFileList.removeLast(); - } - } - - _maxSize = maxSize; - } - //-------------------------------------------------------------------------- - // Private Methods: - //-------------------------------------------------------------------------- - - //-------------------------------------------------------------------------- - // Nested Top-Level Classes or Interfaces - //-------------------------------------------------------------------------- -} \ No newline at end of file diff --git a/java/src/org/apache/log4j/net/JMSAppender.java b/java/src/org/apache/log4j/net/JMSAppender.java deleted file mode 100644 index 60bd5d0..0000000 --- a/java/src/org/apache/log4j/net/JMSAppender.java +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.net; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.spi.ErrorCode; -import org.apache.log4j.spi.LoggingEvent; - -import javax.jms.JMSException; -import javax.jms.ObjectMessage; -import javax.jms.Session; -import javax.jms.Topic; -import javax.jms.TopicConnection; -import javax.jms.TopicConnectionFactory; -import javax.jms.TopicPublisher; -import javax.jms.TopicSession; -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NameNotFoundException; -import javax.naming.NamingException; -import java.util.Properties; - -/** - * A simple appender that publishes events to a JMS Topic. The events - * are serialized and transmitted as JMS message type {@link - * ObjectMessage}. - - *

JMS {@link Topic topics} and {@link TopicConnectionFactory topic - * connection factories} are administered objects that are retrieved - * using JNDI messaging which in turn requires the retreival of a JNDI - * {@link Context}. - - *

There are two common methods for retrieving a JNDI {@link - * Context}. If a file resource named jndi.properties is - * available to the JNDI API, it will use the information found - * therein to retrieve an initial JNDI context. To obtain an initial - * context, your code will simply call: - -

-   InitialContext jndiContext = new InitialContext();
-   
- - *

Calling the no-argument InitialContext() method - * will also work from within Enterprise Java Beans (EJBs) because it - * is part of the EJB contract for application servers to provide each - * bean an environment naming context (ENC). - - *

In the second approach, several predetermined properties are set - * and these properties are passed to the InitialContext - * contructor to connect to the naming service provider. For example, - * to connect to JBoss naming service one would write: - -

-   Properties env = new Properties( );
-   env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
-   env.put(Context.PROVIDER_URL, "jnp://hostname:1099");
-   env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
-   InitialContext jndiContext = new InitialContext(env);
-
- - * where hostname is the host where the JBoss applicaiton - * server is running. - * - *

To connect to the the naming service of Weblogic application - * server one would write: - -

-   Properties env = new Properties( );
-   env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
-   env.put(Context.PROVIDER_URL, "t3://localhost:7001");
-   InitialContext jndiContext = new InitialContext(env);
-
- - *

Other JMS providers will obviously require different values. - * - * The initial JNDI context can be obtained by calling the - * no-argument InitialContext() method in EJBs. Only - * clients running in a separate JVM need to be concerned about the - * jndi.properties file and calling {@link - * InitialContext#InitialContext()} or alternatively correctly - * setting the different properties before calling {@link - * InitialContext#InitialContext(java.util.Hashtable)} method. - - - @author Ceki Gülcü */ -public class JMSAppender extends AppenderSkeleton { - - String securityPrincipalName; - String securityCredentials; - String initialContextFactoryName; - String urlPkgPrefixes; - String providerURL; - String topicBindingName; - String tcfBindingName; - String userName; - String password; - boolean locationInfo; - - TopicConnection topicConnection; - TopicSession topicSession; - TopicPublisher topicPublisher; - - public - JMSAppender() { - } - - /** - The TopicConnectionFactoryBindingName option takes a - string value. Its value will be used to lookup the appropriate - TopicConnectionFactory from the JNDI context. - */ - public - void setTopicConnectionFactoryBindingName(String tcfBindingName) { - this.tcfBindingName = tcfBindingName; - } - - /** - Returns the value of the TopicConnectionFactoryBindingName option. - */ - public - String getTopicConnectionFactoryBindingName() { - return tcfBindingName; - } - - /** - The TopicBindingName option takes a - string value. Its value will be used to lookup the appropriate - Topic from the JNDI context. - */ - public - void setTopicBindingName(String topicBindingName) { - this.topicBindingName = topicBindingName; - } - - /** - Returns the value of the TopicBindingName option. - */ - public - String getTopicBindingName() { - return topicBindingName; - } - - - /** - Returns value of the LocationInfo property which - determines whether location (stack) info is sent to the remote - subscriber. */ - public - boolean getLocationInfo() { - return locationInfo; - } - - /** - * Options are activated and become effective only after calling - * this method.*/ - public void activateOptions() { - TopicConnectionFactory topicConnectionFactory; - - try { - Context jndi; - - LogLog.debug("Getting initial context."); - if(initialContextFactoryName != null) { - Properties env = new Properties( ); - env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName); - if(providerURL != null) { - env.put(Context.PROVIDER_URL, providerURL); - } else { - LogLog.warn("You have set InitialContextFactoryName option but not the " - +"ProviderURL. This is likely to cause problems."); - } - if(urlPkgPrefixes != null) { - env.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes); - } - - if(securityPrincipalName != null) { - env.put(Context.SECURITY_PRINCIPAL, securityPrincipalName); - if(securityCredentials != null) { - env.put(Context.SECURITY_CREDENTIALS, securityCredentials); - } else { - LogLog.warn("You have set SecurityPrincipalName option but not the " - +"SecurityCredentials. This is likely to cause problems."); - } - } - jndi = new InitialContext(env); - } else { - jndi = new InitialContext(); - } - - LogLog.debug("Looking up ["+tcfBindingName+"]"); - topicConnectionFactory = (TopicConnectionFactory) lookup(jndi, tcfBindingName); - LogLog.debug("About to create TopicConnection."); - if(userName != null) { - topicConnection = topicConnectionFactory.createTopicConnection(userName, - password); - } else { - topicConnection = topicConnectionFactory.createTopicConnection(); - } - - LogLog.debug("Creating TopicSession, non-transactional, " - +"in AUTO_ACKNOWLEDGE mode."); - topicSession = topicConnection.createTopicSession(false, - Session.AUTO_ACKNOWLEDGE); - - LogLog.debug("Looking up topic name ["+topicBindingName+"]."); - Topic topic = (Topic) lookup(jndi, topicBindingName); - - LogLog.debug("Creating TopicPublisher."); - topicPublisher = topicSession.createPublisher(topic); - - LogLog.debug("Starting TopicConnection."); - topicConnection.start(); - - jndi.close(); - } catch(JMSException e) { - errorHandler.error("Error while activating options for appender named ["+name+ - "].", e, ErrorCode.GENERIC_FAILURE); - } catch(NamingException e) { - errorHandler.error("Error while activating options for appender named ["+name+ - "].", e, ErrorCode.GENERIC_FAILURE); - } catch(RuntimeException e) { - errorHandler.error("Error while activating options for appender named ["+name+ - "].", e, ErrorCode.GENERIC_FAILURE); - } - } - - protected Object lookup(Context ctx, String name) throws NamingException { - try { - return ctx.lookup(name); - } catch(NameNotFoundException e) { - LogLog.error("Could not find name ["+name+"]."); - throw e; - } - } - - protected boolean checkEntryConditions() { - String fail = null; - - if(this.topicConnection == null) { - fail = "No TopicConnection"; - } else if(this.topicSession == null) { - fail = "No TopicSession"; - } else if(this.topicPublisher == null) { - fail = "No TopicPublisher"; - } - - if(fail != null) { - errorHandler.error(fail +" for JMSAppender named ["+name+"]."); - return false; - } else { - return true; - } - } - - /** - Close this JMSAppender. Closing releases all resources used by the - appender. A closed appender cannot be re-opened. */ - public synchronized void close() { - // The synchronized modifier avoids concurrent append and close operations - - if(this.closed) - return; - - LogLog.debug("Closing appender ["+name+"]."); - this.closed = true; - - try { - if(topicSession != null) - topicSession.close(); - if(topicConnection != null) - topicConnection.close(); - } catch(JMSException e) { - LogLog.error("Error while closing JMSAppender ["+name+"].", e); - } catch(RuntimeException e) { - LogLog.error("Error while closing JMSAppender ["+name+"].", e); - } - // Help garbage collection - topicPublisher = null; - topicSession = null; - topicConnection = null; - } - - /** - This method called by {@link AppenderSkeleton#doAppend} method to - do most of the real appending work. */ - public void append(LoggingEvent event) { - if(!checkEntryConditions()) { - return; - } - - try { - ObjectMessage msg = topicSession.createObjectMessage(); - if(locationInfo) { - event.getLocationInformation(); - } - msg.setObject(event); - topicPublisher.publish(msg); - } catch(JMSException e) { - errorHandler.error("Could not publish message in JMSAppender ["+name+"].", e, - ErrorCode.GENERIC_FAILURE); - } catch(RuntimeException e) { - errorHandler.error("Could not publish message in JMSAppender ["+name+"].", e, - ErrorCode.GENERIC_FAILURE); - } - } - - /** - * Returns the value of the InitialContextFactoryName option. - * See {@link #setInitialContextFactoryName} for more details on the - * meaning of this option. - * */ - public String getInitialContextFactoryName() { - return initialContextFactoryName; - } - - /** - * Setting the InitialContextFactoryName method will cause - * this JMSAppender instance to use the {@link - * InitialContext#InitialContext(Hashtable)} method instead of the - * no-argument constructor. If you set this option, you should also - * at least set the ProviderURL option. - * - *

See also {@link #setProviderURL(String)}. - * */ - public void setInitialContextFactoryName(String initialContextFactoryName) { - this.initialContextFactoryName = initialContextFactoryName; - } - - public String getProviderURL() { - return providerURL; - } - - public void setProviderURL(String providerURL) { - this.providerURL = providerURL; - } - - String getURLPkgPrefixes( ) { - return urlPkgPrefixes; - } - - public void setURLPkgPrefixes(String urlPkgPrefixes ) { - this.urlPkgPrefixes = urlPkgPrefixes; - } - - public String getSecurityCredentials() { - return securityCredentials; - } - - public void setSecurityCredentials(String securityCredentials) { - this.securityCredentials = securityCredentials; - } - - - public String getSecurityPrincipalName() { - return securityPrincipalName; - } - - public void setSecurityPrincipalName(String securityPrincipalName) { - this.securityPrincipalName = securityPrincipalName; - } - - public String getUserName() { - return userName; - } - - /** - * The user name to use when {@link - * TopicConnectionFactory#createTopicConnection(String, String) - * creating a topic session}. If you set this option, you should - * also set the Password option. See {@link - * #setPassword(String)}. - * */ - public void setUserName(String userName) { - this.userName = userName; - } - - public String getPassword() { - return password; - } - - /** - * The paswword to use when creating a topic session. - */ - public void setPassword(String password) { - this.password = password; - } - - - /** - If true, the information sent to the remote subscriber will - include caller's location information. By default no location - information is sent to the subscriber. */ - public void setLocationInfo(boolean locationInfo) { - this.locationInfo = locationInfo; - } - - /** - * Returns the TopicConnection used for this appender. Only valid after - * activateOptions() method has been invoked. - */ - protected TopicConnection getTopicConnection() { - return topicConnection; - } - - /** - * Returns the TopicSession used for this appender. Only valid after - * activateOptions() method has been invoked. - */ - protected TopicSession getTopicSession() { - return topicSession; - } - - /** - * Returns the TopicPublisher used for this appender. Only valid after - * activateOptions() method has been invoked. - */ - protected TopicPublisher getTopicPublisher() { - return topicPublisher; - } - - /** - * The JMSAppender sends serialized events and consequently does not - * require a layout. - */ - public boolean requiresLayout() { - return false; - } -} diff --git a/java/src/org/apache/log4j/net/JMSSink.java b/java/src/org/apache/log4j/net/JMSSink.java deleted file mode 100644 index 6a02831..0000000 --- a/java/src/org/apache/log4j/net/JMSSink.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.net; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.xml.DOMConfigurator; - -import javax.jms.JMSException; -import javax.jms.ObjectMessage; -import javax.jms.Session; -import javax.jms.Topic; -import javax.jms.TopicConnection; -import javax.jms.TopicConnectionFactory; -import javax.jms.TopicSession; -import javax.jms.TopicSubscriber; -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NameNotFoundException; -import javax.naming.NamingException; -import java.io.BufferedReader; -import java.io.InputStreamReader; - -/** - * A simple application that consumes logging events sent by a {@link - * JMSAppender}. - * - * - * @author Ceki Gülcü - * */ -public class JMSSink implements javax.jms.MessageListener { - - static Logger logger = Logger.getLogger(JMSSink.class); - - static public void main(String[] args) throws Exception { - if(args.length != 5) { - usage("Wrong number of arguments."); - } - - String tcfBindingName = args[0]; - String topicBindingName = args[1]; - String username = args[2]; - String password = args[3]; - - - String configFile = args[4]; - - if(configFile.endsWith(".xml")) { - DOMConfigurator.configure(configFile); - } else { - PropertyConfigurator.configure(configFile); - } - - new JMSSink(tcfBindingName, topicBindingName, username, password); - - BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); - // Loop until the word "exit" is typed - System.out.println("Type \"exit\" to quit JMSSink."); - while(true){ - String s = stdin.readLine( ); - if (s.equalsIgnoreCase("exit")) { - System.out.println("Exiting. Kill the application if it does not exit " - + "due to daemon threads."); - return; - } - } - } - - public JMSSink( String tcfBindingName, String topicBindingName, String username, - String password) { - - try { - Context ctx = new InitialContext(); - TopicConnectionFactory topicConnectionFactory; - topicConnectionFactory = (TopicConnectionFactory) lookup(ctx, - tcfBindingName); - - TopicConnection topicConnection = - topicConnectionFactory.createTopicConnection(username, - password); - topicConnection.start(); - - TopicSession topicSession = topicConnection.createTopicSession(false, - Session.AUTO_ACKNOWLEDGE); - - Topic topic = (Topic)ctx.lookup(topicBindingName); - - TopicSubscriber topicSubscriber = topicSession.createSubscriber(topic); - - topicSubscriber.setMessageListener(this); - - } catch(JMSException e) { - logger.error("Could not read JMS message.", e); - } catch(NamingException e) { - logger.error("Could not read JMS message.", e); - } catch(RuntimeException e) { - logger.error("Could not read JMS message.", e); - } - } - - public void onMessage(javax.jms.Message message) { - LoggingEvent event; - Logger remoteLogger; - - try { - if(message instanceof ObjectMessage) { - ObjectMessage objectMessage = (ObjectMessage) message; - event = (LoggingEvent) objectMessage.getObject(); - remoteLogger = Logger.getLogger(event.getLoggerName()); - remoteLogger.callAppenders(event); - } else { - logger.warn("Received message is of type "+message.getJMSType() - +", was expecting ObjectMessage."); - } - } catch(JMSException jmse) { - logger.error("Exception thrown while processing incoming message.", - jmse); - } - } - - - protected static Object lookup(Context ctx, String name) throws NamingException { - try { - return ctx.lookup(name); - } catch(NameNotFoundException e) { - logger.error("Could not find name ["+name+"]."); - throw e; - } - } - - static void usage(String msg) { - System.err.println(msg); - System.err.println("Usage: java " + JMSSink.class.getName() - + " TopicConnectionFactoryBindingName TopicBindingName username password configFile"); - System.exit(1); - } -} diff --git a/java/src/org/apache/log4j/net/SMTPAppender.java b/java/src/org/apache/log4j/net/SMTPAppender.java deleted file mode 100644 index 7350474..0000000 --- a/java/src/org/apache/log4j/net/SMTPAppender.java +++ /dev/null @@ -1,787 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.net; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.Layout; -import org.apache.log4j.Level; -import org.apache.log4j.helpers.CyclicBuffer; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.helpers.OptionConverter; -import org.apache.log4j.spi.ErrorCode; -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.spi.OptionHandler; -import org.apache.log4j.spi.TriggeringEventEvaluator; -import org.apache.log4j.xml.UnrecognizedElementHandler; -import org.w3c.dom.Element; - -import javax.mail.Authenticator; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.PasswordAuthentication; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.AddressException; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.InternetHeaders; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; -import javax.mail.internet.MimeUtility; -import java.io.ByteArrayOutputStream; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.util.Date; -import java.util.Properties; - -/** - Send an e-mail when a specific logging event occurs, typically on - errors or fatal errors. - -

The number of logging events delivered in this e-mail depend on - the value of BufferSize option. The - SMTPAppender keeps only the last - BufferSize logging events in its cyclic buffer. This - keeps memory requirements at a reasonable level while still - delivering useful application context. - - By default, an email message will be sent when an ERROR or higher - severity message is appended. The triggering criteria can be - modified by setting the evaluatorClass property with the name - of a class implementing TriggeringEventEvaluator, setting the evaluator - property with an instance of TriggeringEventEvaluator or - nesting a triggeringPolicy element where the specified - class implements TriggeringEventEvaluator. - - This class has implemented UnrecognizedElementHandler since 1.2.15. - - Since 1.2.16, SMTP over SSL is supported by setting SMTPProtocol to "smpts". - - @author Ceki Gülcü - @since 1.0 */ -public class SMTPAppender extends AppenderSkeleton - implements UnrecognizedElementHandler { - private String to; - /** - * Comma separated list of cc recipients. - */ - private String cc; - /** - * Comma separated list of bcc recipients. - */ - private String bcc; - private String from; - /** - * Comma separated list of replyTo addresses. - */ - private String replyTo; - private String subject; - private String smtpHost; - private String smtpUsername; - private String smtpPassword; - private String smtpProtocol; - private int smtpPort = -1; - private boolean smtpDebug = false; - private int bufferSize = 512; - private boolean locationInfo = false; - private boolean sendOnClose = false; - - protected CyclicBuffer cb = new CyclicBuffer(bufferSize); - protected Message msg; - - protected TriggeringEventEvaluator evaluator; - - - - /** - The default constructor will instantiate the appender with a - {@link TriggeringEventEvaluator} that will trigger on events with - level ERROR or higher.*/ - public - SMTPAppender() { - this(new DefaultEvaluator()); - } - - - /** - Use evaluator passed as parameter as the {@link - TriggeringEventEvaluator} for this SMTPAppender. */ - public - SMTPAppender(TriggeringEventEvaluator evaluator) { - this.evaluator = evaluator; - } - - - /** - Activate the specified options, such as the smtp host, the - recipient, from, etc. */ - public - void activateOptions() { - Session session = createSession(); - msg = new MimeMessage(session); - - try { - addressMessage(msg); - if(subject != null) { - try { - msg.setSubject(MimeUtility.encodeText(subject, "UTF-8", null)); - } catch(UnsupportedEncodingException ex) { - LogLog.error("Unable to encode SMTP subject", ex); - } - } - } catch(MessagingException e) { - LogLog.error("Could not activate SMTPAppender options.", e ); - } - - if (evaluator instanceof OptionHandler) { - ((OptionHandler) evaluator).activateOptions(); - } - } - - /** - * Address message. - * @param msg message, may not be null. - * @throws MessagingException thrown if error addressing message. - * @since 1.2.14 - */ - protected void addressMessage(final Message msg) throws MessagingException { - if (from != null) { - msg.setFrom(getAddress(from)); - } else { - msg.setFrom(); - } - - //Add ReplyTo addresses if defined. - if (replyTo != null && replyTo.length() > 0) { - msg.setReplyTo(parseAddress(replyTo)); - } - - if (to != null && to.length() > 0) { - msg.setRecipients(Message.RecipientType.TO, parseAddress(to)); - } - - //Add CC receipients if defined. - if (cc != null && cc.length() > 0) { - msg.setRecipients(Message.RecipientType.CC, parseAddress(cc)); - } - - //Add BCC receipients if defined. - if (bcc != null && bcc.length() > 0) { - msg.setRecipients(Message.RecipientType.BCC, parseAddress(bcc)); - } - } - - /** - * Create mail session. - * @return mail session, may not be null. - * @since 1.2.14 - */ - protected Session createSession() { - Properties props = null; - try { - props = new Properties (System.getProperties()); - } catch(SecurityException ex) { - props = new Properties(); - } - - String prefix = "mail.smtp"; - if (smtpProtocol != null) { - props.put("mail.transport.protocol", smtpProtocol); - prefix = "mail." + smtpProtocol; - } - if (smtpHost != null) { - props.put(prefix + ".host", smtpHost); - } - if (smtpPort > 0) { - props.put(prefix + ".port", String.valueOf(smtpPort)); - } - - Authenticator auth = null; - if(smtpPassword != null && smtpUsername != null) { - props.put(prefix + ".auth", "true"); - auth = new Authenticator() { - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(smtpUsername, smtpPassword); - } - }; - } - Session session = Session.getInstance(props, auth); - if (smtpProtocol != null) { - session.setProtocolForAddress("rfc822", smtpProtocol); - } - if (smtpDebug) { - session.setDebug(smtpDebug); - } - return session; - } - - /** - Perform SMTPAppender specific appending actions, mainly adding - the event to a cyclic buffer and checking if the event triggers - an e-mail to be sent. */ - public - void append(LoggingEvent event) { - - if(!checkEntryConditions()) { - return; - } - - event.getThreadName(); - event.getNDC(); - event.getMDCCopy(); - if(locationInfo) { - event.getLocationInformation(); - } - event.getRenderedMessage(); - event.getThrowableStrRep(); - cb.add(event); - if(evaluator.isTriggeringEvent(event)) { - sendBuffer(); - } - } - - /** - This method determines if there is a sense in attempting to append. - -

It checks whether there is a set output target and also if - there is a set layout. If these checks fail, then the boolean - value false is returned. */ - protected - boolean checkEntryConditions() { - if(this.msg == null) { - errorHandler.error("Message object not configured."); - return false; - } - - if(this.evaluator == null) { - errorHandler.error("No TriggeringEventEvaluator is set for appender ["+ - name+"]."); - return false; - } - - - if(this.layout == null) { - errorHandler.error("No layout set for appender named ["+name+"]."); - return false; - } - return true; - } - - - synchronized - public - void close() { - this.closed = true; - if (sendOnClose && cb.length() > 0) { - sendBuffer(); - } - } - - InternetAddress getAddress(String addressStr) { - try { - return new InternetAddress(addressStr); - } catch(AddressException e) { - errorHandler.error("Could not parse address ["+addressStr+"].", e, - ErrorCode.ADDRESS_PARSE_FAILURE); - return null; - } - } - - InternetAddress[] parseAddress(String addressStr) { - try { - return InternetAddress.parse(addressStr, true); - } catch(AddressException e) { - errorHandler.error("Could not parse address ["+addressStr+"].", e, - ErrorCode.ADDRESS_PARSE_FAILURE); - return null; - } - } - - /** - Returns value of the To option. - */ - public - String getTo() { - return to; - } - - - /** - The SMTPAppender requires a {@link - org.apache.log4j.Layout layout}. */ - public - boolean requiresLayout() { - return true; - } - - /** - * Layout body of email message. - * @since 1.2.16 - */ - protected String formatBody() { - - // Note: this code already owns the monitor for this - // appender. This frees us from needing to synchronize on 'cb'. - - StringBuffer sbuf = new StringBuffer(); - String t = layout.getHeader(); - if(t != null) - sbuf.append(t); - int len = cb.length(); - for(int i = 0; i < len; i++) { - //sbuf.append(MimeUtility.encodeText(layout.format(cb.get()))); - LoggingEvent event = cb.get(); - sbuf.append(layout.format(event)); - if(layout.ignoresThrowable()) { - String[] s = event.getThrowableStrRep(); - if (s != null) { - for(int j = 0; j < s.length; j++) { - sbuf.append(s[j]); - sbuf.append(Layout.LINE_SEP); - } - } - } - } - t = layout.getFooter(); - if(t != null) { - sbuf.append(t); - } - - return sbuf.toString(); - } - - /** - Send the contents of the cyclic buffer as an e-mail message. - */ - protected - void sendBuffer() { - - try { - String s = formatBody(); - boolean allAscii = true; - for(int i = 0; i < s.length() && allAscii; i++) { - allAscii = s.charAt(i) <= 0x7F; - } - MimeBodyPart part; - if (allAscii) { - part = new MimeBodyPart(); - part.setContent(s, layout.getContentType()); - } else { - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - Writer writer = new OutputStreamWriter( - MimeUtility.encode(os, "quoted-printable"), "UTF-8"); - writer.write(s); - writer.close(); - InternetHeaders headers = new InternetHeaders(); - headers.setHeader("Content-Type", layout.getContentType() + "; charset=UTF-8"); - headers.setHeader("Content-Transfer-Encoding", "quoted-printable"); - part = new MimeBodyPart(headers, os.toByteArray()); - } catch(Exception ex) { - StringBuffer sbuf = new StringBuffer(s); - for (int i = 0; i < sbuf.length(); i++) { - if (sbuf.charAt(i) >= 0x80) { - sbuf.setCharAt(i, '?'); - } - } - part = new MimeBodyPart(); - part.setContent(sbuf.toString(), layout.getContentType()); - } - } - - - - Multipart mp = new MimeMultipart(); - mp.addBodyPart(part); - msg.setContent(mp); - - msg.setSentDate(new Date()); - Transport.send(msg); - } catch(MessagingException e) { - LogLog.error("Error occured while sending e-mail notification.", e); - } catch(RuntimeException e) { - LogLog.error("Error occured while sending e-mail notification.", e); - } - } - - - - /** - Returns value of the EvaluatorClass option. - */ - public - String getEvaluatorClass() { - return evaluator == null ? null : evaluator.getClass().getName(); - } - - /** - Returns value of the From option. - */ - public - String getFrom() { - return from; - } - - /** - Get the reply addresses. - @return reply addresses as comma separated string, may be null. - @since 1.2.16 - */ - public - String getReplyTo() { - return replyTo; - } - - /** - Returns value of the Subject option. - */ - public - String getSubject() { - return subject; - } - - /** - The From option takes a string value which should be a - e-mail address of the sender. - */ - public - void setFrom(String from) { - this.from = from; - } - - /** - Set the e-mail addresses to which replies should be directed. - @param addresses reply addresses as comma separated string, may be null. - @since 1.2.16 - */ - public - void setReplyTo(final String addresses) { - this.replyTo = addresses; - } - - - /** - The Subject option takes a string value which should be a - the subject of the e-mail message. - */ - public - void setSubject(String subject) { - this.subject = subject; - } - - - /** - The BufferSize option takes a positive integer - representing the maximum number of logging events to collect in a - cyclic buffer. When the BufferSize is reached, - oldest events are deleted as new events are added to the - buffer. By default the size of the cyclic buffer is 512 events. - */ - public - void setBufferSize(int bufferSize) { - this.bufferSize = bufferSize; - cb.resize(bufferSize); - } - - /** - The SMTPHost option takes a string value which should be a - the host name of the SMTP server that will send the e-mail message. - */ - public - void setSMTPHost(String smtpHost) { - this.smtpHost = smtpHost; - } - - /** - Returns value of the SMTPHost option. - */ - public - String getSMTPHost() { - return smtpHost; - } - - /** - The To option takes a string value which should be a - comma separated list of e-mail address of the recipients. - */ - public - void setTo(String to) { - this.to = to; - } - - - - /** - Returns value of the BufferSize option. - */ - public - int getBufferSize() { - return bufferSize; - } - - /** - The EvaluatorClass option takes a string value - representing the name of the class implementing the {@link - TriggeringEventEvaluator} interface. A corresponding object will - be instantiated and assigned as the triggering event evaluator - for the SMTPAppender. - */ - public - void setEvaluatorClass(String value) { - evaluator = (TriggeringEventEvaluator) - OptionConverter.instantiateByClassName(value, - TriggeringEventEvaluator.class, - evaluator); - } - - - /** - The LocationInfo option takes a boolean value. By - default, it is set to false which means there will be no effort - to extract the location information related to the event. As a - result, the layout that formats the events as they are sent out - in an e-mail is likely to place the wrong location information - (if present in the format). - -

Location information extraction is comparatively very slow and - should be avoided unless performance is not a concern. - */ - public - void setLocationInfo(boolean locationInfo) { - this.locationInfo = locationInfo; - } - - /** - Returns value of the LocationInfo option. - */ - public - boolean getLocationInfo() { - return locationInfo; - } - - /** - Set the cc recipient addresses. - @param addresses recipient addresses as comma separated string, may be null. - @since 1.2.14 - */ - public void setCc(final String addresses) { - this.cc = addresses; - } - - /** - Get the cc recipient addresses. - @return recipient addresses as comma separated string, may be null. - @since 1.2.14 - */ - public String getCc() { - return cc; - } - - /** - Set the bcc recipient addresses. - @param addresses recipient addresses as comma separated string, may be null. - @since 1.2.14 - */ - public void setBcc(final String addresses) { - this.bcc = addresses; - } - - /** - Get the bcc recipient addresses. - @return recipient addresses as comma separated string, may be null. - @since 1.2.14 - */ - public String getBcc() { - return bcc; - } - - /** - * The SmtpPassword option takes a string value which should be the password required to authenticate against - * the mail server. - * @param password password, may be null. - * @since 1.2.14 - */ - public void setSMTPPassword(final String password) { - this.smtpPassword = password; - } - - /** - * The SmtpUsername option takes a string value which should be the username required to authenticate against - * the mail server. - * @param username user name, may be null. - * @since 1.2.14 - */ - public void setSMTPUsername(final String username) { - this.smtpUsername = username; - } - - /** - * Setting the SmtpDebug option to true will cause the mail session to log its server interaction to stdout. - * This can be useful when debuging the appender but should not be used during production because username and - * password information is included in the output. - * @param debug debug flag. - * @since 1.2.14 - */ - public void setSMTPDebug(final boolean debug) { - this.smtpDebug = debug; - } - - /** - * Get SMTP password. - * @return SMTP password, may be null. - * @since 1.2.14 - */ - public String getSMTPPassword() { - return smtpPassword; - } - - /** - * Get SMTP user name. - * @return SMTP user name, may be null. - * @since 1.2.14 - */ - public String getSMTPUsername() { - return smtpUsername; - } - - /** - * Get SMTP debug. - * @return SMTP debug flag. - * @since 1.2.14 - */ - public boolean getSMTPDebug() { - return smtpDebug; - } - - /** - * Sets triggering evaluator. - * @param trigger triggering event evaluator. - * @since 1.2.15 - */ - public final void setEvaluator(final TriggeringEventEvaluator trigger) { - if (trigger == null) { - throw new NullPointerException("trigger"); - } - this.evaluator = trigger; - } - - /** - * Get triggering evaluator. - * @return triggering event evaluator. - * @since 1.2.15 - */ - public final TriggeringEventEvaluator getEvaluator() { - return evaluator; - } - - /** {@inheritDoc} - * @since 1.2.15 - */ - public boolean parseUnrecognizedElement(final Element element, - final Properties props) throws Exception { - if ("triggeringPolicy".equals(element.getNodeName())) { - Object triggerPolicy = - org.apache.log4j.xml.DOMConfigurator.parseElement( - element, props, TriggeringEventEvaluator.class); - if (triggerPolicy instanceof TriggeringEventEvaluator) { - setEvaluator((TriggeringEventEvaluator) triggerPolicy); - } - return true; - } - - return false; - } - - /** - * Get transport protocol. - * Typically null or "smtps". - * - * @return transport protocol, may be null. - * @since 1.2.16 - */ - public final String getSMTPProtocol() { - return smtpProtocol; - } - - /** - * Set transport protocol. - * Typically null or "smtps". - * - * @param val transport protocol, may be null. - * @since 1.2.16 - */ - public final void setSMTPProtocol(final String val) { - smtpProtocol = val; - } - - /** - * Get port. - * - * @return port, negative values indicate use of default ports for protocol. - * @since 1.2.16 - */ - public final int getSMTPPort() { - return smtpPort; - } - - /** - * Set port. - * - * @param val port, negative values indicate use of default ports for protocol. - * @since 1.2.16 - */ - public final void setSMTPPort(final int val) { - smtpPort = val; - } - - /** - * Get sendOnClose. - * - * @return if true all buffered logging events will be sent when the appender is closed. - * @since 1.2.16 - */ - public final boolean getSendOnClose() { - return sendOnClose; - } - - /** - * Set sendOnClose. - * - * @param val if true all buffered logging events will be sent when appender is closed. - * @since 1.2.16 - */ - public final void setSendOnClose(final boolean val) { - sendOnClose = val; - } - -} - -class DefaultEvaluator implements TriggeringEventEvaluator { - /** - Is this event the e-mail triggering event? - -

This method returns true, if the event level - has ERROR level or higher. Otherwise it returns - false. */ - public - boolean isTriggeringEvent(LoggingEvent event) { - return event.getLevel().isGreaterOrEqual(Level.ERROR); - } -} diff --git a/java/src/org/apache/log4j/net/SimpleSocketServer.java b/java/src/org/apache/log4j/net/SimpleSocketServer.java deleted file mode 100644 index c15aa3c..0000000 --- a/java/src/org/apache/log4j/net/SimpleSocketServer.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.net; - -import java.net.ServerSocket; -import java.net.Socket; - -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; -import org.apache.log4j.xml.DOMConfigurator; - - -/** - * A simple {@link SocketNode} based server. - * -

-   Usage: java org.apache.log4j.net.SimpleSocketServer port configFile
-
-   where port is a part number where the server listens and
-   configFile is a configuration file fed to the {@link
-   PropertyConfigurator} or to {@link DOMConfigurator} if an XML file.
-   
- * - * @author Ceki Gülcü - * - * @since 0.8.4 - * */ -public class SimpleSocketServer { - - static Logger cat = Logger.getLogger(SimpleSocketServer.class); - - static int port; - - public - static - void main(String argv[]) { - if(argv.length == 2) { - init(argv[0], argv[1]); - } else { - usage("Wrong number of arguments."); - } - - try { - cat.info("Listening on port " + port); - ServerSocket serverSocket = new ServerSocket(port); - while(true) { - cat.info("Waiting to accept a new client."); - Socket socket = serverSocket.accept(); - cat.info("Connected to client at " + socket.getInetAddress()); - cat.info("Starting new socket node."); - new Thread(new SocketNode(socket, - LogManager.getLoggerRepository()),"SimpleSocketServer-" + port).start(); - } - } catch(Exception e) { - e.printStackTrace(); - } - } - - - static void usage(String msg) { - System.err.println(msg); - System.err.println( - "Usage: java " +SimpleSocketServer.class.getName() + " port configFile"); - System.exit(1); - } - - static void init(String portStr, String configFile) { - try { - port = Integer.parseInt(portStr); - } catch(java.lang.NumberFormatException e) { - e.printStackTrace(); - usage("Could not interpret port number ["+ portStr +"]."); - } - - if(configFile.endsWith(".xml")) { - DOMConfigurator.configure(configFile); - } else { - PropertyConfigurator.configure(configFile); - } - } -} diff --git a/java/src/org/apache/log4j/net/SocketAppender.java b/java/src/org/apache/log4j/net/SocketAppender.java deleted file mode 100644 index 855b057..0000000 --- a/java/src/org/apache/log4j/net/SocketAppender.java +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contributors: Dan MacDonald - -package org.apache.log4j.net; - -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.InterruptedIOException; -import java.net.InetAddress; -import java.net.Socket; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.spi.ErrorCode; -import org.apache.log4j.spi.LoggingEvent; - -/** - Sends {@link LoggingEvent} objects to a remote a log server, - usually a {@link SocketNode}. - -

The SocketAppender has the following properties: - -

    - -

  • If sent to a {@link SocketNode}, remote logging is - non-intrusive as far as the log event is concerned. In other - words, the event will be logged with the same time stamp, {@link - org.apache.log4j.NDC}, location info as if it were logged locally by - the client. - -

  • SocketAppenders do not use a layout. They ship a - serialized {@link LoggingEvent} object to the server side. - -

  • Remote logging uses the TCP protocol. Consequently, if - the server is reachable, then log events will eventually arrive - at the server. - -

  • If the remote server is down, the logging requests are - simply dropped. However, if and when the server comes back up, - then event transmission is resumed transparently. This - transparent reconneciton is performed by a connector - thread which periodically attempts to connect to the server. - -

  • Logging events are automatically buffered by the - native TCP implementation. This means that if the link to server - is slow but still faster than the rate of (log) event production - by the client, the client will not be affected by the slow - network connection. However, if the network connection is slower - then the rate of event production, then the client can only - progress at the network rate. In particular, if the network link - to the the server is down, the client will be blocked. - -

    On the other hand, if the network link is up, but the server - is down, the client will not be blocked when making log requests - but the log events will be lost due to server unavailability. - -

  • Even if a SocketAppender is no longer - attached to any category, it will not be garbage collected in - the presence of a connector thread. A connector thread exists - only if the connection to the server is down. To avoid this - garbage collection problem, you should {@link #close} the the - SocketAppender explicitly. See also next item. - -

    Long lived applications which create/destroy many - SocketAppender instances should be aware of this - garbage collection problem. Most other applications can safely - ignore it. - -

  • If the JVM hosting the SocketAppender exits - before the SocketAppender is closed either - explicitly or subsequent to garbage collection, then there might - be untransmitted data in the pipe which might be lost. This is a - common problem on Windows based systems. - -

    To avoid lost data, it is usually sufficient to {@link - #close} the SocketAppender either explicitly or by - calling the {@link org.apache.log4j.LogManager#shutdown} method - before exiting the application. - - -

- - @author Ceki Gülcü - @since 0.8.4 */ - -public class SocketAppender extends AppenderSkeleton { - - /** - The default port number of remote logging server (4560). - @since 1.2.15 - */ - static public final int DEFAULT_PORT = 4560; - - /** - The default reconnection delay (30000 milliseconds or 30 seconds). - */ - static final int DEFAULT_RECONNECTION_DELAY = 30000; - - /** - We remember host name as String in addition to the resolved - InetAddress so that it can be returned via getOption(). - */ - String remoteHost; - - /** - * The MulticastDNS zone advertised by a SocketAppender - */ - public static final String ZONE = "_log4j_obj_tcpconnect_appender.local."; - - InetAddress address; - int port = DEFAULT_PORT; - ObjectOutputStream oos; - int reconnectionDelay = DEFAULT_RECONNECTION_DELAY; - boolean locationInfo = false; - private String application; - - private Connector connector; - - int counter = 0; - - // reset the ObjectOutputStream every 70 calls - //private static final int RESET_FREQUENCY = 70; - private static final int RESET_FREQUENCY = 1; - private boolean advertiseViaMulticastDNS; - private ZeroConfSupport zeroConf; - - public SocketAppender() { - } - - /** - Connects to remote server at address and port. - */ - public SocketAppender(InetAddress address, int port) { - this.address = address; - this.remoteHost = address.getHostName(); - this.port = port; - connect(address, port); - } - - /** - Connects to remote server at host and port. - */ - public SocketAppender(String host, int port) { - this.port = port; - this.address = getAddressByName(host); - this.remoteHost = host; - connect(address, port); - } - - /** - Connect to the specified RemoteHost and Port. - */ - public void activateOptions() { - if (advertiseViaMulticastDNS) { - zeroConf = new ZeroConfSupport(ZONE, port, getName()); - zeroConf.advertise(); - } - connect(address, port); - } - - /** - * Close this appender. - * - *

This will mark the appender as closed and call then {@link - * #cleanUp} method. - * */ - synchronized public void close() { - if(closed) - return; - - this.closed = true; - if (advertiseViaMulticastDNS) { - zeroConf.unadvertise(); - } - - cleanUp(); - } - - /** - * Drop the connection to the remote host and release the underlying - * connector thread if it has been created - * */ - public void cleanUp() { - if(oos != null) { - try { - oos.close(); - } catch(IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("Could not close oos.", e); - } - oos = null; - } - if(connector != null) { - //LogLog.debug("Interrupting the connector."); - connector.interrupted = true; - connector = null; // allow gc - } - } - - void connect(InetAddress address, int port) { - if(this.address == null) - return; - try { - // First, close the previous connection if any. - cleanUp(); - oos = new ObjectOutputStream(new Socket(address, port).getOutputStream()); - } catch(IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - String msg = "Could not connect to remote log4j server at [" - +address.getHostName()+"]."; - if(reconnectionDelay > 0) { - msg += " We will try again later."; - fireConnector(); // fire the connector thread - } else { - msg += " We are not retrying."; - errorHandler.error(msg, e, ErrorCode.GENERIC_FAILURE); - } - LogLog.error(msg); - } - } - - - public void append(LoggingEvent event) { - if(event == null) - return; - - if(address==null) { - errorHandler.error("No remote host is set for SocketAppender named \""+ - this.name+"\"."); - return; - } - - if(oos != null) { - try { - - if(locationInfo) { - event.getLocationInformation(); - } - if (application != null) { - event.setProperty("application", application); - } - event.getNDC(); - event.getThreadName(); - event.getMDCCopy(); - event.getRenderedMessage(); - event.getThrowableStrRep(); - - oos.writeObject(event); - //LogLog.debug("=========Flushing."); - oos.flush(); - if(++counter >= RESET_FREQUENCY) { - counter = 0; - // Failing to reset the object output stream every now and - // then creates a serious memory leak. - //System.err.println("Doing oos.reset()"); - oos.reset(); - } - } catch(IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - oos = null; - LogLog.warn("Detected problem with connection: "+e); - if(reconnectionDelay > 0) { - fireConnector(); - } else { - errorHandler.error("Detected problem with connection, not reconnecting.", e, - ErrorCode.GENERIC_FAILURE); - } - } - } - } - - public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) { - this.advertiseViaMulticastDNS = advertiseViaMulticastDNS; - } - - public boolean isAdvertiseViaMulticastDNS() { - return advertiseViaMulticastDNS; - } - - void fireConnector() { - if(connector == null) { - LogLog.debug("Starting a new connector thread."); - connector = new Connector(); - connector.setDaemon(true); - connector.setPriority(Thread.MIN_PRIORITY); - connector.start(); - } - } - - static - InetAddress getAddressByName(String host) { - try { - return InetAddress.getByName(host); - } catch(Exception e) { - if (e instanceof InterruptedIOException || e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - LogLog.error("Could not find address of ["+host+"].", e); - return null; - } - } - - /** - * The SocketAppender does not use a layout. Hence, this method - * returns false. - * */ - public boolean requiresLayout() { - return false; - } - - /** - * The RemoteHost option takes a string value which should be - * the host name of the server where a {@link SocketNode} is - * running. - * */ - public void setRemoteHost(String host) { - address = getAddressByName(host); - remoteHost = host; - } - - /** - Returns value of the RemoteHost option. - */ - public String getRemoteHost() { - return remoteHost; - } - - /** - The Port option takes a positive integer representing - the port where the server is waiting for connections. - */ - public void setPort(int port) { - this.port = port; - } - - /** - Returns value of the Port option. - */ - public int getPort() { - return port; - } - - /** - The LocationInfo option takes a boolean value. If true, - the information sent to the remote host will include location - information. By default no location information is sent to the server. - */ - public void setLocationInfo(boolean locationInfo) { - this.locationInfo = locationInfo; - } - - /** - Returns value of the LocationInfo option. - */ - public boolean getLocationInfo() { - return locationInfo; - } - - /** - * The App option takes a string value which should be the name of the - * application getting logged. - * If property was already set (via system property), don't set here. - * @since 1.2.15 - */ - public void setApplication(String lapp) { - this.application = lapp; - } - - /** - * Returns value of the Application option. - * @since 1.2.15 - */ - public String getApplication() { - return application; - } - - /** - The ReconnectionDelay option takes a positive integer - representing the number of milliseconds to wait between each - failed connection attempt to the server. The default value of - this option is 30000 which corresponds to 30 seconds. - -

Setting this option to zero turns off reconnection - capability. - */ - public void setReconnectionDelay(int delay) { - this.reconnectionDelay = delay; - } - - /** - Returns value of the ReconnectionDelay option. - */ - public int getReconnectionDelay() { - return reconnectionDelay; - } - - /** - The Connector will reconnect when the server becomes available - again. It does this by attempting to open a new connection every - reconnectionDelay milliseconds. - -

It stops trying whenever a connection is established. It will - restart to try reconnect to the server when previously open - connection is droppped. - - @author Ceki Gülcü - @since 0.8.4 - */ - class Connector extends Thread { - - boolean interrupted = false; - - public - void run() { - Socket socket; - while(!interrupted) { - try { - sleep(reconnectionDelay); - LogLog.debug("Attempting connection to "+address.getHostName()); - socket = new Socket(address, port); - synchronized(this) { - oos = new ObjectOutputStream(socket.getOutputStream()); - connector = null; - LogLog.debug("Connection established. Exiting connector thread."); - break; - } - } catch(InterruptedException e) { - LogLog.debug("Connector interrupted. Leaving loop."); - return; - } catch(java.net.ConnectException e) { - LogLog.debug("Remote host "+address.getHostName() - +" refused connection."); - } catch(IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.debug("Could not connect to " + address.getHostName()+ - ". Exception is " + e); - } - } - //LogLog.debug("Exiting Connector.run() method."); - } - - /** - public - void finalize() { - LogLog.debug("Connector finalize() has been called."); - } - */ - } - -} diff --git a/java/src/org/apache/log4j/net/SocketHubAppender.java b/java/src/org/apache/log4j/net/SocketHubAppender.java deleted file mode 100644 index 665c58a..0000000 --- a/java/src/org/apache/log4j/net/SocketHubAppender.java +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.net; - -import java.io.IOException; -import java.io.InterruptedIOException; -import java.io.ObjectOutputStream; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.util.Vector; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.helpers.CyclicBuffer; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.spi.LoggingEvent; - -/** - Sends {@link LoggingEvent} objects to a set of remote log servers, - usually a {@link SocketNode SocketNodes}. - -

Acts just like {@link SocketAppender} except that instead of - connecting to a given remote log server, - SocketHubAppender accepts connections from the remote - log servers as clients. It can accept more than one connection. - When a log event is received, the event is sent to the set of - currently connected remote log servers. Implemented this way it does - not require any update to the configuration file to send data to - another remote log server. The remote log server simply connects to - the host and port the SocketHubAppender is running on. - -

The SocketHubAppender does not store events such - that the remote side will events that arrived after the - establishment of its connection. Once connected, events arrive in - order as guaranteed by the TCP protocol. - -

This implementation borrows heavily from the {@link - SocketAppender}. - -

The SocketHubAppender has the following characteristics: - -

    - -

  • If sent to a {@link SocketNode}, logging is non-intrusive as - far as the log event is concerned. In other words, the event will be - logged with the same time stamp, {@link org.apache.log4j.NDC}, - location info as if it were logged locally. - -

  • SocketHubAppender does not use a layout. It - ships a serialized {@link LoggingEvent} object to the remote side. - -

  • SocketHubAppender relies on the TCP - protocol. Consequently, if the remote side is reachable, then log - events will eventually arrive at remote client. - -

  • If no remote clients are attached, the logging requests are - simply dropped. - -

  • Logging events are automatically buffered by the - native TCP implementation. This means that if the link to remote - client is slow but still faster than the rate of (log) event - production, the application will not be affected by the slow network - connection. However, if the network connection is slower then the - rate of event production, then the local application can only - progress at the network rate. In particular, if the network link to - the the remote client is down, the application will be blocked. - -

    On the other hand, if the network link is up, but the remote - client is down, the client will not be blocked when making log - requests but the log events will be lost due to client - unavailability. - -

    The single remote client case extends to multiple clients - connections. The rate of logging will be determined by the slowest - link. - -

  • If the JVM hosting the SocketHubAppender exits - before the SocketHubAppender is closed either - explicitly or subsequent to garbage collection, then there might - be untransmitted data in the pipe which might be lost. This is a - common problem on Windows based systems. - -

    To avoid lost data, it is usually sufficient to {@link #close} - the SocketHubAppender either explicitly or by calling - the {@link org.apache.log4j.LogManager#shutdown} method before - exiting the application. - -

- - @author Mark Womack */ - -public class SocketHubAppender extends AppenderSkeleton { - - /** - The default port number of the ServerSocket will be created on. */ - static final int DEFAULT_PORT = 4560; - - private int port = DEFAULT_PORT; - private Vector oosList = new Vector(); - private ServerMonitor serverMonitor = null; - private boolean locationInfo = false; - private CyclicBuffer buffer = null; - private String application; - private boolean advertiseViaMulticastDNS; - private ZeroConfSupport zeroConf; - - /** - * The MulticastDNS zone advertised by a SocketHubAppender - */ - public static final String ZONE = "_log4j_obj_tcpaccept_appender.local."; - - - public SocketHubAppender() { } - - /** - Connects to remote server at address and port. */ - public - SocketHubAppender(int _port) { - port = _port; - startServer(); - } - - /** - Set up the socket server on the specified port. */ - public - void activateOptions() { - if (advertiseViaMulticastDNS) { - zeroConf = new ZeroConfSupport(ZONE, port, getName()); - zeroConf.advertise(); - } - startServer(); - } - - /** - Close this appender. -

This will mark the appender as closed and - call then {@link #cleanUp} method. */ - synchronized - public - void close() { - if(closed) - return; - - LogLog.debug("closing SocketHubAppender " + getName()); - this.closed = true; - if (advertiseViaMulticastDNS) { - zeroConf.unadvertise(); - } - cleanUp(); - - LogLog.debug("SocketHubAppender " + getName() + " closed"); - } - - /** - Release the underlying ServerMonitor thread, and drop the connections - to all connected remote servers. */ - public - void cleanUp() { - // stop the monitor thread - LogLog.debug("stopping ServerSocket"); - serverMonitor.stopMonitor(); - serverMonitor = null; - - // close all of the connections - LogLog.debug("closing client connections"); - while (oosList.size() != 0) { - ObjectOutputStream oos = (ObjectOutputStream)oosList.elementAt(0); - if(oos != null) { - try { - oos.close(); - } catch(InterruptedIOException e) { - Thread.currentThread().interrupt(); - LogLog.error("could not close oos.", e); - } catch(IOException e) { - LogLog.error("could not close oos.", e); - } - - oosList.removeElementAt(0); - } - } - } - - /** - Append an event to all of current connections. */ - public - void append(LoggingEvent event) { - if (event != null) { - // set up location info if requested - if (locationInfo) { - event.getLocationInformation(); - } - if (application != null) { - event.setProperty("application", application); - } - event.getNDC(); - event.getThreadName(); - event.getMDCCopy(); - event.getRenderedMessage(); - event.getThrowableStrRep(); - - if (buffer != null) { - buffer.add(event); - } - } - - // if no event or no open connections, exit now - if ((event == null) || (oosList.size() == 0)) { - return; - } - - // loop through the current set of open connections, appending the event to each - for (int streamCount = 0; streamCount < oosList.size(); streamCount++) { - - ObjectOutputStream oos = null; - try { - oos = (ObjectOutputStream)oosList.elementAt(streamCount); - } - catch (ArrayIndexOutOfBoundsException e) { - // catch this, but just don't assign a value - // this should not really occur as this method is - // the only one that can remove oos's (besides cleanUp). - } - - // list size changed unexpectedly? Just exit the append. - if (oos == null) - break; - - try { - oos.writeObject(event); - oos.flush(); - // Failing to reset the object output stream every now and - // then creates a serious memory leak. - // right now we always reset. TODO - set up frequency counter per oos? - oos.reset(); - } - catch(IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - // there was an io exception so just drop the connection - oosList.removeElementAt(streamCount); - LogLog.debug("dropped connection"); - - // decrement to keep the counter in place (for loop always increments) - streamCount--; - } - } - } - - /** - The SocketHubAppender does not use a layout. Hence, this method returns - false. */ - public - boolean requiresLayout() { - return false; - } - - /** - The Port option takes a positive integer representing - the port where the server is waiting for connections. */ - public - void setPort(int _port) { - port = _port; - } - - /** - * The App option takes a string value which should be the name of the application getting logged. If property was already set (via system - * property), don't set here. - */ - public - void setApplication(String lapp) { - this.application = lapp; - } - - /** - * Returns value of the Application option. - */ - public - String getApplication() { - return application; - } - - /** - Returns value of the Port option. */ - public - int getPort() { - return port; - } - - /** - * The BufferSize option takes a positive integer representing the number of events this appender will buffer and send to newly connected - * clients. - */ - public - void setBufferSize(int _bufferSize) { - buffer = new CyclicBuffer(_bufferSize); - } - - /** - * Returns value of the bufferSize option. - */ - public - int getBufferSize() { - if (buffer == null) { - return 0; - } else { - return buffer.getMaxSize(); - } - } - - /** - The LocationInfo option takes a boolean value. If true, - the information sent to the remote host will include location - information. By default no location information is sent to the server. */ - public - void setLocationInfo(boolean _locationInfo) { - locationInfo = _locationInfo; - } - - /** - Returns value of the LocationInfo option. */ - public - boolean getLocationInfo() { - return locationInfo; - } - - public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) { - this.advertiseViaMulticastDNS = advertiseViaMulticastDNS; - } - - public boolean isAdvertiseViaMulticastDNS() { - return advertiseViaMulticastDNS; - } - - /** - Start the ServerMonitor thread. */ - private - void startServer() { - serverMonitor = new ServerMonitor(port, oosList); - } - - /** - * Creates a server socket to accept connections. - * @param socketPort port on which the socket should listen, may be zero. - * @return new socket. - * @throws IOException IO error when opening the socket. - */ - protected ServerSocket createServerSocket(final int socketPort) throws IOException { - return new ServerSocket(socketPort); - } - - /** - This class is used internally to monitor a ServerSocket - and register new connections in a vector passed in the - constructor. */ - private - class ServerMonitor implements Runnable { - private int port; - private Vector oosList; - private boolean keepRunning; - private Thread monitorThread; - - /** - Create a thread and start the monitor. */ - public - ServerMonitor(int _port, Vector _oosList) { - port = _port; - oosList = _oosList; - keepRunning = true; - monitorThread = new Thread(this); - monitorThread.setDaemon(true); - monitorThread.setName("SocketHubAppender-Monitor-" + port); - monitorThread.start(); - } - - /** - Stops the monitor. This method will not return until - the thread has finished executing. */ - public - synchronized - void stopMonitor() { - if (keepRunning) { - LogLog.debug("server monitor thread shutting down"); - keepRunning = false; - try { - monitorThread.join(); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - // do nothing? - } - - // release the thread - monitorThread = null; - LogLog.debug("server monitor thread shut down"); - } - } - - private - void sendCachedEvents(ObjectOutputStream stream) throws IOException { - if (buffer != null) { - for (int i = 0; i < buffer.length(); i++) { - stream.writeObject(buffer.get(i)); - } - stream.flush(); - stream.reset(); - } - } - - /** - Method that runs, monitoring the ServerSocket and adding connections as - they connect to the socket. */ - public - void run() { - ServerSocket serverSocket = null; - try { - serverSocket = createServerSocket(port); - serverSocket.setSoTimeout(1000); - } - catch (Exception e) { - if (e instanceof InterruptedIOException || e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - LogLog.error("exception setting timeout, shutting down server socket.", e); - keepRunning = false; - return; - } - - try { - try { - serverSocket.setSoTimeout(1000); - } - catch (SocketException e) { - LogLog.error("exception setting timeout, shutting down server socket.", e); - return; - } - - while (keepRunning) { - Socket socket = null; - try { - socket = serverSocket.accept(); - } - catch (InterruptedIOException e) { - // timeout occurred, so just loop - } - catch (SocketException e) { - LogLog.error("exception accepting socket, shutting down server socket.", e); - keepRunning = false; - } - catch (IOException e) { - LogLog.error("exception accepting socket.", e); - } - - // if there was a socket accepted - if (socket != null) { - try { - InetAddress remoteAddress = socket.getInetAddress(); - LogLog.debug("accepting connection from " + remoteAddress.getHostName() - + " (" + remoteAddress.getHostAddress() + ")"); - - // create an ObjectOutputStream - ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); - if (buffer != null && buffer.length() > 0) { - sendCachedEvents(oos); - } - - // add it to the oosList. OK since Vector is synchronized. - oosList.addElement(oos); - } catch (IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("exception creating output stream on socket.", e); - } - } - } - } - finally { - // close the socket - try { - serverSocket.close(); - } catch(InterruptedIOException e) { - Thread.currentThread().interrupt(); - } catch (IOException e) { - // do nothing with it? - } - } - } - } -} - diff --git a/java/src/org/apache/log4j/net/SocketNode.java b/java/src/org/apache/log4j/net/SocketNode.java deleted file mode 100644 index e977f13..0000000 --- a/java/src/org/apache/log4j/net/SocketNode.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.net; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.io.ObjectInputStream; -import java.net.Socket; - -import org.apache.log4j.Logger; -import org.apache.log4j.spi.LoggerRepository; -import org.apache.log4j.spi.LoggingEvent; - -// Contributors: Moses Hohman - -/** - Read {@link LoggingEvent} objects sent from a remote client using - Sockets (TCP). These logging events are logged according to local - policy, as if they were generated locally. - -

For example, the socket node might decide to log events to a - local file and also resent them to a second socket node. - - @author Ceki Gülcü - - @since 0.8.4 -*/ -public class SocketNode implements Runnable { - - Socket socket; - LoggerRepository hierarchy; - ObjectInputStream ois; - - static Logger logger = Logger.getLogger(SocketNode.class); - - public SocketNode(Socket socket, LoggerRepository hierarchy) { - this.socket = socket; - this.hierarchy = hierarchy; - try { - ois = new ObjectInputStream( - new BufferedInputStream(socket.getInputStream())); - } catch(InterruptedIOException e) { - Thread.currentThread().interrupt(); - logger.error("Could not open ObjectInputStream to "+socket, e); - } catch(IOException e) { - logger.error("Could not open ObjectInputStream to "+socket, e); - } catch(RuntimeException e) { - logger.error("Could not open ObjectInputStream to "+socket, e); - } - } - - //public - //void finalize() { - //System.err.println("-------------------------Finalize called"); - // System.err.flush(); - //} - - public void run() { - LoggingEvent event; - Logger remoteLogger; - - try { - if (ois != null) { - while(true) { - // read an event from the wire - event = (LoggingEvent) ois.readObject(); - // get a logger from the hierarchy. The name of the logger is taken to be the name contained in the event. - remoteLogger = hierarchy.getLogger(event.getLoggerName()); - //event.logger = remoteLogger; - // apply the logger-level filter - if(event.getLevel().isGreaterOrEqual(remoteLogger.getEffectiveLevel())) { - // finally log the event as if was generated locally - remoteLogger.callAppenders(event); - } - } - } - } catch(java.io.EOFException e) { - logger.info("Caught java.io.EOFException closing conneciton."); - } catch(java.net.SocketException e) { - logger.info("Caught java.net.SocketException closing conneciton."); - } catch(InterruptedIOException e) { - Thread.currentThread().interrupt(); - logger.info("Caught java.io.InterruptedIOException: "+e); - logger.info("Closing connection."); - } catch(IOException e) { - logger.info("Caught java.io.IOException: "+e); - logger.info("Closing connection."); - } catch(Exception e) { - logger.error("Unexpected exception. Closing conneciton.", e); - } finally { - if (ois != null) { - try { - ois.close(); - } catch(Exception e) { - logger.info("Could not close connection.", e); - } - } - if (socket != null) { - try { - socket.close(); - } catch(InterruptedIOException e) { - Thread.currentThread().interrupt(); - } catch(IOException ex) { - } - } - } - } -} diff --git a/java/src/org/apache/log4j/net/SocketServer.java b/java/src/org/apache/log4j/net/SocketServer.java deleted file mode 100644 index 8ea3cb7..0000000 --- a/java/src/org/apache/log4j/net/SocketServer.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.net; - -import java.io.File; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.Hashtable; - -import org.apache.log4j.Hierarchy; -import org.apache.log4j.Level; -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; -import org.apache.log4j.spi.LoggerRepository; -import org.apache.log4j.spi.RootLogger; - - -/** - A {@link SocketNode} based server that uses a different hierarchy - for each client. - -

-     Usage: java org.apache.log4j.net.SocketServer port configFile configDir
-
-     where port is a part number where the server listens,
-           configFile is a configuration file fed to the {@link PropertyConfigurator} and
-           configDir is a path to a directory containing configuration files, possibly one for each client host.
-     
- -

The configFile is used to configure the log4j - default hierarchy that the SocketServer will use to - report on its actions. - -

When a new connection is opened from a previously unknown - host, say foo.bar.net, then the - SocketServer will search for a configuration file - called foo.bar.net.lcf under the directory - configDir that was passed as the third argument. If - the file can be found, then a new hierarchy is instantiated and - configured using the configuration file - foo.bar.net.lcf. If and when the host - foo.bar.net opens another connection to the server, - then the previously configured hierarchy is used. - -

In case there is no file called foo.bar.net.lcf - under the directory configDir, then the - generic hierarchy is used. The generic hierarchy is - configured using a configuration file called - generic.lcf under the configDir - directory. If no such file exists, then the generic hierarchy will be - identical to the log4j default hierarchy. - -

Having different client hosts log using different hierarchies - ensures the total independence of the clients with respect to - their logging settings. - -

Currently, the hierarchy that will be used for a given request - depends on the IP address of the client host. For example, two - separate applicatons running on the same host and logging to the - same server will share the same hierarchy. This is perfectly safe - except that it might not provide the right amount of independence - between applications. The SocketServer is intended - as an example to be enhanced in order to implement more elaborate - policies. - - - @author Ceki Gülcü - - @since 1.0 */ - -public class SocketServer { - - static String GENERIC = "generic"; - static String CONFIG_FILE_EXT = ".lcf"; - - static Logger cat = Logger.getLogger(SocketServer.class); - static SocketServer server; - static int port; - - // key=inetAddress, value=hierarchy - Hashtable hierarchyMap; - LoggerRepository genericHierarchy; - File dir; - - public - static - void main(String argv[]) { - if(argv.length == 3) - init(argv[0], argv[1], argv[2]); - else - usage("Wrong number of arguments."); - - try { - cat.info("Listening on port " + port); - ServerSocket serverSocket = new ServerSocket(port); - while(true) { - cat.info("Waiting to accept a new client."); - Socket socket = serverSocket.accept(); - InetAddress inetAddress = socket.getInetAddress(); - cat.info("Connected to client at " + inetAddress); - - LoggerRepository h = (LoggerRepository) server.hierarchyMap.get(inetAddress); - if(h == null) { - h = server.configureHierarchy(inetAddress); - } - - cat.info("Starting new socket node."); - new Thread(new SocketNode(socket, h)).start(); - } - } - catch(Exception e) { - e.printStackTrace(); - } - } - - - static - void usage(String msg) { - System.err.println(msg); - System.err.println( - "Usage: java " +SocketServer.class.getName() + " port configFile directory"); - System.exit(1); - } - - static - void init(String portStr, String configFile, String dirStr) { - try { - port = Integer.parseInt(portStr); - } - catch(java.lang.NumberFormatException e) { - e.printStackTrace(); - usage("Could not interpret port number ["+ portStr +"]."); - } - - PropertyConfigurator.configure(configFile); - - File dir = new File(dirStr); - if(!dir.isDirectory()) { - usage("["+dirStr+"] is not a directory."); - } - server = new SocketServer(dir); - } - - - public - SocketServer(File directory) { - this.dir = directory; - hierarchyMap = new Hashtable(11); - } - - // This method assumes that there is no hiearchy for inetAddress - // yet. It will configure one and return it. - LoggerRepository configureHierarchy(InetAddress inetAddress) { - cat.info("Locating configuration file for "+inetAddress); - // We assume that the toSting method of InetAddress returns is in - // the format hostname/d1.d2.d3.d4 e.g. torino/192.168.1.1 - String s = inetAddress.toString(); - int i = s.indexOf("/"); - if(i == -1) { - cat.warn("Could not parse the inetAddress ["+inetAddress+ - "]. Using default hierarchy."); - return genericHierarchy(); - } else { - String key = s.substring(0, i); - - File configFile = new File(dir, key+CONFIG_FILE_EXT); - if(configFile.exists()) { - Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG)); - hierarchyMap.put(inetAddress, h); - - new PropertyConfigurator().doConfigure(configFile.getAbsolutePath(), h); - - return h; - } else { - cat.warn("Could not find config file ["+configFile+"]."); - return genericHierarchy(); - } - } - } - - LoggerRepository genericHierarchy() { - if(genericHierarchy == null) { - File f = new File(dir, GENERIC+CONFIG_FILE_EXT); - if(f.exists()) { - genericHierarchy = new Hierarchy(new RootLogger(Level.DEBUG)); - new PropertyConfigurator().doConfigure(f.getAbsolutePath(), genericHierarchy); - } else { - cat.warn("Could not find config file ["+f+ - "]. Will use the default hierarchy."); - genericHierarchy = LogManager.getLoggerRepository(); - } - } - return genericHierarchy; - } -} diff --git a/java/src/org/apache/log4j/net/SyslogAppender.java b/java/src/org/apache/log4j/net/SyslogAppender.java deleted file mode 100644 index 6ab7edd..0000000 --- a/java/src/org/apache/log4j/net/SyslogAppender.java +++ /dev/null @@ -1,536 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.net; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.Layout; -import org.apache.log4j.helpers.SyslogQuietWriter; -import org.apache.log4j.helpers.SyslogWriter; -import org.apache.log4j.spi.LoggingEvent; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.io.IOException; - -// Contributors: Yves Bossel -// Christopher Taylor - -/** - Use SyslogAppender to send log messages to a remote syslog daemon. - - @author Ceki Gülcü - @author Anders Kristensen - */ -public class SyslogAppender extends AppenderSkeleton { - // The following constants are extracted from a syslog.h file - // copyrighted by the Regents of the University of California - // I hope nobody at Berkley gets offended. - - /** Kernel messages */ - final static public int LOG_KERN = 0; - /** Random user-level messages */ - final static public int LOG_USER = 1<<3; - /** Mail system */ - final static public int LOG_MAIL = 2<<3; - /** System daemons */ - final static public int LOG_DAEMON = 3<<3; - /** security/authorization messages */ - final static public int LOG_AUTH = 4<<3; - /** messages generated internally by syslogd */ - final static public int LOG_SYSLOG = 5<<3; - - /** line printer subsystem */ - final static public int LOG_LPR = 6<<3; - /** network news subsystem */ - final static public int LOG_NEWS = 7<<3; - /** UUCP subsystem */ - final static public int LOG_UUCP = 8<<3; - /** clock daemon */ - final static public int LOG_CRON = 9<<3; - /** security/authorization messages (private) */ - final static public int LOG_AUTHPRIV = 10<<3; - /** ftp daemon */ - final static public int LOG_FTP = 11<<3; - - // other codes through 15 reserved for system use - /** reserved for local use */ - final static public int LOG_LOCAL0 = 16<<3; - /** reserved for local use */ - final static public int LOG_LOCAL1 = 17<<3; - /** reserved for local use */ - final static public int LOG_LOCAL2 = 18<<3; - /** reserved for local use */ - final static public int LOG_LOCAL3 = 19<<3; - /** reserved for local use */ - final static public int LOG_LOCAL4 = 20<<3; - /** reserved for local use */ - final static public int LOG_LOCAL5 = 21<<3; - /** reserved for local use */ - final static public int LOG_LOCAL6 = 22<<3; - /** reserved for local use*/ - final static public int LOG_LOCAL7 = 23<<3; - - protected static final int SYSLOG_HOST_OI = 0; - protected static final int FACILITY_OI = 1; - - static final String TAB = " "; - - // Have LOG_USER as default - int syslogFacility = LOG_USER; - String facilityStr; - boolean facilityPrinting = false; - - //SyslogTracerPrintWriter stp; - SyslogQuietWriter sqw; - String syslogHost; - - /** - * If true, the appender will generate the HEADER (timestamp and host name) - * part of the syslog packet. - * @since 1.2.15 - */ - private boolean header = false; - /** - * Date format used if header = true. - * @since 1.2.15 - */ - private final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd HH:mm:ss ", Locale.ENGLISH); - /** - * Host name used to identify messages from this appender. - * @since 1.2.15 - */ - private String localHostname; - - /** - * Set to true after the header of the layout has been sent or if it has none. - */ - private boolean layoutHeaderChecked = false; - - public - SyslogAppender() { - this.initSyslogFacilityStr(); - } - - public - SyslogAppender(Layout layout, int syslogFacility) { - this.layout = layout; - this.syslogFacility = syslogFacility; - this.initSyslogFacilityStr(); - } - - public - SyslogAppender(Layout layout, String syslogHost, int syslogFacility) { - this(layout, syslogFacility); - setSyslogHost(syslogHost); - } - - /** - Release any resources held by this SyslogAppender. - - @since 0.8.4 - */ - synchronized - public - void close() { - closed = true; - if (sqw != null) { - try { - if (layoutHeaderChecked && layout != null && layout.getFooter() != null) { - sendLayoutMessage(layout.getFooter()); - } - sqw.close(); - sqw = null; - } catch(java.io.InterruptedIOException e) { - Thread.currentThread().interrupt(); - sqw = null; - } catch(IOException e) { - sqw = null; - } - } - } - - private - void initSyslogFacilityStr() { - facilityStr = getFacilityString(this.syslogFacility); - - if (facilityStr == null) { - System.err.println("\"" + syslogFacility + - "\" is an unknown syslog facility. Defaulting to \"USER\"."); - this.syslogFacility = LOG_USER; - facilityStr = "user:"; - } else { - facilityStr += ":"; - } - } - - /** - Returns the specified syslog facility as a lower-case String, - e.g. "kern", "user", etc. - */ - public - static - String getFacilityString(int syslogFacility) { - switch(syslogFacility) { - case LOG_KERN: return "kern"; - case LOG_USER: return "user"; - case LOG_MAIL: return "mail"; - case LOG_DAEMON: return "daemon"; - case LOG_AUTH: return "auth"; - case LOG_SYSLOG: return "syslog"; - case LOG_LPR: return "lpr"; - case LOG_NEWS: return "news"; - case LOG_UUCP: return "uucp"; - case LOG_CRON: return "cron"; - case LOG_AUTHPRIV: return "authpriv"; - case LOG_FTP: return "ftp"; - case LOG_LOCAL0: return "local0"; - case LOG_LOCAL1: return "local1"; - case LOG_LOCAL2: return "local2"; - case LOG_LOCAL3: return "local3"; - case LOG_LOCAL4: return "local4"; - case LOG_LOCAL5: return "local5"; - case LOG_LOCAL6: return "local6"; - case LOG_LOCAL7: return "local7"; - default: return null; - } - } - - /** - Returns the integer value corresponding to the named syslog - facility, or -1 if it couldn't be recognized. - - @param facilityName one of the strings KERN, USER, MAIL, DAEMON, - AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, LOCAL0, - LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. - The matching is case-insensitive. - - @since 1.1 - */ - public - static - int getFacility(String facilityName) { - if(facilityName != null) { - facilityName = facilityName.trim(); - } - if("KERN".equalsIgnoreCase(facilityName)) { - return LOG_KERN; - } else if("USER".equalsIgnoreCase(facilityName)) { - return LOG_USER; - } else if("MAIL".equalsIgnoreCase(facilityName)) { - return LOG_MAIL; - } else if("DAEMON".equalsIgnoreCase(facilityName)) { - return LOG_DAEMON; - } else if("AUTH".equalsIgnoreCase(facilityName)) { - return LOG_AUTH; - } else if("SYSLOG".equalsIgnoreCase(facilityName)) { - return LOG_SYSLOG; - } else if("LPR".equalsIgnoreCase(facilityName)) { - return LOG_LPR; - } else if("NEWS".equalsIgnoreCase(facilityName)) { - return LOG_NEWS; - } else if("UUCP".equalsIgnoreCase(facilityName)) { - return LOG_UUCP; - } else if("CRON".equalsIgnoreCase(facilityName)) { - return LOG_CRON; - } else if("AUTHPRIV".equalsIgnoreCase(facilityName)) { - return LOG_AUTHPRIV; - } else if("FTP".equalsIgnoreCase(facilityName)) { - return LOG_FTP; - } else if("LOCAL0".equalsIgnoreCase(facilityName)) { - return LOG_LOCAL0; - } else if("LOCAL1".equalsIgnoreCase(facilityName)) { - return LOG_LOCAL1; - } else if("LOCAL2".equalsIgnoreCase(facilityName)) { - return LOG_LOCAL2; - } else if("LOCAL3".equalsIgnoreCase(facilityName)) { - return LOG_LOCAL3; - } else if("LOCAL4".equalsIgnoreCase(facilityName)) { - return LOG_LOCAL4; - } else if("LOCAL5".equalsIgnoreCase(facilityName)) { - return LOG_LOCAL5; - } else if("LOCAL6".equalsIgnoreCase(facilityName)) { - return LOG_LOCAL6; - } else if("LOCAL7".equalsIgnoreCase(facilityName)) { - return LOG_LOCAL7; - } else { - return -1; - } - } - - - private void splitPacket(final String header, final String packet) { - int byteCount = packet.getBytes().length; - // - // if packet is less than RFC 3164 limit - // of 1024 bytes, then write it - // (must allow for up 5to 5 characters in the PRI section - // added by SyslogQuietWriter) - if (byteCount <= 1019) { - sqw.write(packet); - } else { - int split = header.length() + (packet.length() - header.length())/2; - splitPacket(header, packet.substring(0, split) + "..."); - splitPacket(header, header + "..." + packet.substring(split)); - } - } - - public - void append(LoggingEvent event) { - - if(!isAsSevereAsThreshold(event.getLevel())) - return; - - // We must not attempt to append if sqw is null. - if(sqw == null) { - errorHandler.error("No syslog host is set for SyslogAppedender named \""+ - this.name+"\"."); - return; - } - - if (!layoutHeaderChecked) { - if (layout != null && layout.getHeader() != null) { - sendLayoutMessage(layout.getHeader()); - } - layoutHeaderChecked = true; - } - - String hdr = getPacketHeader(event.timeStamp); - String packet; - if (layout == null) { - packet = String.valueOf(event.getMessage()); - } else { - packet = layout.format(event); - } - if(facilityPrinting || hdr.length() > 0) { - StringBuffer buf = new StringBuffer(hdr); - if(facilityPrinting) { - buf.append(facilityStr); - } - buf.append(packet); - packet = buf.toString(); - } - - sqw.setLevel(event.getLevel().getSyslogEquivalent()); - // - // if message has a remote likelihood of exceeding 1024 bytes - // when encoded, consider splitting message into multiple packets - if (packet.length() > 256) { - splitPacket(hdr, packet); - } else { - sqw.write(packet); - } - - if (layout == null || layout.ignoresThrowable()) { - String[] s = event.getThrowableStrRep(); - if (s != null) { - for(int i = 0; i < s.length; i++) { - if (s[i].startsWith("\t")) { - sqw.write(hdr+TAB+s[i].substring(1)); - } else { - sqw.write(hdr+s[i]); - } - } - } - } - } - - /** - This method returns immediately as options are activated when they - are set. - */ - public - void activateOptions() { - if (header) { - getLocalHostname(); - } - if (layout != null && layout.getHeader() != null) { - sendLayoutMessage(layout.getHeader()); - } - layoutHeaderChecked = true; - } - - /** - The SyslogAppender requires a layout. Hence, this method returns - true. - - @since 0.8.4 */ - public - boolean requiresLayout() { - return true; - } - - /** - The SyslogHost option is the name of the the syslog host - where log output should go. A non-default port can be specified by - appending a colon and port number to a host name, - an IPv4 address or an IPv6 address enclosed in square brackets. - - WARNING If the SyslogHost is not set, then this appender - will fail. - */ - public - void setSyslogHost(final String syslogHost) { - this.sqw = new SyslogQuietWriter(new SyslogWriter(syslogHost), - syslogFacility, errorHandler); - //this.stp = new SyslogTracerPrintWriter(sqw); - this.syslogHost = syslogHost; - } - - /** - Returns the value of the SyslogHost option. - */ - public - String getSyslogHost() { - return syslogHost; - } - - /** - Set the syslog facility. This is the Facility option. - -

The facilityName parameter must be one of the - strings KERN, USER, MAIL, DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP, - CRON, AUTHPRIV, FTP, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, - LOCAL5, LOCAL6, LOCAL7. Case is unimportant. - - @since 0.8.1 */ - public - void setFacility(String facilityName) { - if(facilityName == null) - return; - - syslogFacility = getFacility(facilityName); - if (syslogFacility == -1) { - System.err.println("["+facilityName + - "] is an unknown syslog facility. Defaulting to [USER]."); - syslogFacility = LOG_USER; - } - - this.initSyslogFacilityStr(); - - // If there is already a sqw, make it use the new facility. - if(sqw != null) { - sqw.setSyslogFacility(this.syslogFacility); - } - } - - /** - Returns the value of the Facility option. - */ - public - String getFacility() { - return getFacilityString(syslogFacility); - } - - /** - If the FacilityPrinting option is set to true, the printed - message will include the facility name of the application. It is - false by default. - */ - public - void setFacilityPrinting(boolean on) { - facilityPrinting = on; - } - - /** - Returns the value of the FacilityPrinting option. - */ - public - boolean getFacilityPrinting() { - return facilityPrinting; - } - - /** - * If true, the appender will generate the HEADER part (that is, timestamp and host name) - * of the syslog packet. Default value is false for compatibility with existing behavior, - * however should be true unless there is a specific justification. - * @since 1.2.15 - */ - public final boolean getHeader() { - return header; - } - - /** - * Returns whether the appender produces the HEADER part (that is, timestamp and host name) - * of the syslog packet. - * @since 1.2.15 - */ - public final void setHeader(final boolean val) { - header = val; - } - - /** - * Get the host name used to identify this appender. - * @return local host name - * @since 1.2.15 - */ - private String getLocalHostname() { - if (localHostname == null) { - try { - InetAddress addr = InetAddress.getLocalHost(); - localHostname = addr.getHostName(); - } catch (UnknownHostException uhe) { - localHostname = "UNKNOWN_HOST"; - } - } - return localHostname; - } - - /** - * Gets HEADER portion of packet. - * @param timeStamp number of milliseconds after the standard base time. - * @return HEADER portion of packet, will be zero-length string if header is false. - * @since 1.2.15 - */ - private String getPacketHeader(final long timeStamp) { - if (header) { - StringBuffer buf = new StringBuffer(dateFormat.format(new Date(timeStamp))); - // RFC 3164 says leading space, not leading zero on days 1-9 - if (buf.charAt(4) == '0') { - buf.setCharAt(4, ' '); - } - buf.append(getLocalHostname()); - buf.append(' '); - return buf.toString(); - } - return ""; - } - - /** - * Set header or footer of layout. - * @param msg message body, may not be null. - */ - private void sendLayoutMessage(final String msg) { - if (sqw != null) { - String packet = msg; - String hdr = getPacketHeader(new Date().getTime()); - if(facilityPrinting || hdr.length() > 0) { - StringBuffer buf = new StringBuffer(hdr); - if(facilityPrinting) { - buf.append(facilityStr); - } - buf.append(msg); - packet = buf.toString(); - } - sqw.setLevel(6); - sqw.write(packet); - } - } -} diff --git a/java/src/org/apache/log4j/net/TelnetAppender.java b/java/src/org/apache/log4j/net/TelnetAppender.java deleted file mode 100644 index 23847b5..0000000 --- a/java/src/org/apache/log4j/net/TelnetAppender.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.net; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.spi.LoggingEvent; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.InterruptedIOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Vector; - -/** -

The TelnetAppender is a log4j appender that specializes in - writing to a read-only socket. The output is provided in a - telnet-friendly way so that a log can be monitored over TCP/IP. - Clients using telnet connect to the socket and receive log data. - This is handy for remote monitoring, especially when monitoring a - servlet. - -

Here is a list of the available configuration options: - - - - - - - - - - - - - - -
NameRequirementDescriptionSample Value
PortoptionalThis parameter determines the port to use for announcing log events. The default port is 23 (telnet).5875
- - @author Jay Funnell -*/ - -public class TelnetAppender extends AppenderSkeleton { - - private SocketHandler sh; - private int port = 23; - - /** - This appender requires a layout to format the text to the - attached client(s). */ - public boolean requiresLayout() { - return true; - } - - /** all of the options have been set, create the socket handler and - wait for connections. */ - public void activateOptions() { - try { - sh = new SocketHandler(port); - sh.start(); - } - catch(InterruptedIOException e) { - Thread.currentThread().interrupt(); - e.printStackTrace(); - } catch(IOException e) { - e.printStackTrace(); - } catch(RuntimeException e) { - e.printStackTrace(); - } - super.activateOptions(); - } - - public - int getPort() { - return port; - } - - public - void setPort(int port) { - this.port = port; - } - - - /** shuts down the appender. */ - public void close() { - if (sh != null) { - sh.close(); - try { - sh.join(); - } catch(InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } - - /** Handles a log event. For this appender, that means writing the - message to each connected client. */ - protected void append(LoggingEvent event) { - if(sh != null) { - sh.send(layout.format(event)); - if(layout.ignoresThrowable()) { - String[] s = event.getThrowableStrRep(); - if (s != null) { - StringBuffer buf = new StringBuffer(); - for(int i = 0; i < s.length; i++) { - buf.append(s[i]); - buf.append("\r\n"); - } - sh.send(buf.toString()); - } - } - } - } - - //---------------------------------------------------------- SocketHandler: - - /** The SocketHandler class is used to accept connections from - clients. It is threaded so that clients can connect/disconnect - asynchronously. */ - protected class SocketHandler extends Thread { - - private Vector writers = new Vector(); - private Vector connections = new Vector(); - private ServerSocket serverSocket; - private int MAX_CONNECTIONS = 20; - - public void finalize() { - close(); - } - - /** - * make sure we close all network connections when this handler is destroyed. - * @since 1.2.15 - */ - public void close() { - synchronized(this) { - for(Enumeration e = connections.elements();e.hasMoreElements();) { - try { - ((Socket)e.nextElement()).close(); - } catch(InterruptedIOException ex) { - Thread.currentThread().interrupt(); - } catch(IOException ex) { - } catch(RuntimeException ex) { - } - } - } - - try { - serverSocket.close(); - } catch(InterruptedIOException ex) { - Thread.currentThread().interrupt(); - } catch(IOException ex) { - } catch(RuntimeException ex) { - } - } - - /** sends a message to each of the clients in telnet-friendly output. */ - public synchronized void send(final String message) { - Iterator ce = connections.iterator(); - for(Iterator e = writers.iterator();e.hasNext();) { - ce.next(); - PrintWriter writer = (PrintWriter)e.next(); - writer.print(message); - if(writer.checkError()) { - ce.remove(); - e.remove(); - } - } - } - - /** - Continually accepts client connections. Client connections - are refused when MAX_CONNECTIONS is reached. - */ - public void run() { - while(!serverSocket.isClosed()) { - try { - Socket newClient = serverSocket.accept(); - PrintWriter pw = new PrintWriter(newClient.getOutputStream()); - if(connections.size() < MAX_CONNECTIONS) { - synchronized(this) { - connections.addElement(newClient); - writers.addElement(pw); - pw.print("TelnetAppender v1.0 (" + connections.size() - + " active connections)\r\n\r\n"); - pw.flush(); - } - } else { - pw.print("Too many connections.\r\n"); - pw.flush(); - newClient.close(); - } - } catch(Exception e) { - if (e instanceof InterruptedIOException || e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - if (!serverSocket.isClosed()) { - LogLog.error("Encountered error while in SocketHandler loop.", e); - } - break; - } - } - - try { - serverSocket.close(); - } catch(InterruptedIOException ex) { - Thread.currentThread().interrupt(); - } catch(IOException ex) { - } - } - - public SocketHandler(int port) throws IOException { - serverSocket = new ServerSocket(port); - setName("TelnetAppender-" + getName() + "-" + port); - } - - } -} diff --git a/java/src/org/apache/log4j/net/ZeroConfSupport.java b/java/src/org/apache/log4j/net/ZeroConfSupport.java deleted file mode 100644 index 32ac971..0000000 --- a/java/src/org/apache/log4j/net/ZeroConfSupport.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.net; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; - -import org.apache.log4j.helpers.LogLog; - -public class ZeroConfSupport { - private static Object jmDNS = initializeJMDNS(); - - Object serviceInfo; - private static Class jmDNSClass; - private static Class serviceInfoClass; - - public ZeroConfSupport(String zone, int port, String name, Map properties) { - //if version 3 is available, use it to constuct a serviceInfo instance, otherwise support the version1 API - boolean isVersion3 = false; - try { - //create method is in version 3, not version 1 - jmDNSClass.getMethod("create", null); - isVersion3 = true; - } catch (NoSuchMethodException e) { - //no-op - } - - if (isVersion3) { - LogLog.debug("using JmDNS version 3 to construct serviceInfo instance"); - serviceInfo = buildServiceInfoVersion3(zone, port, name, properties); - } else { - LogLog.debug("using JmDNS version 1.0 to construct serviceInfo instance"); - serviceInfo = buildServiceInfoVersion1(zone, port, name, properties); - } - } - - public ZeroConfSupport(String zone, int port, String name) { - this(zone, port, name, new HashMap()); - } - - private static Object createJmDNSVersion1() - { - try { - return jmDNSClass.newInstance(); - } catch (InstantiationException e) { - LogLog.warn("Unable to instantiate JMDNS", e); - } catch (IllegalAccessException e) { - LogLog.warn("Unable to instantiate JMDNS", e); - } - return null; - } - - private static Object createJmDNSVersion3() - { - try { - Method jmDNSCreateMethod = jmDNSClass.getMethod("create", null); - return jmDNSCreateMethod.invoke(null, null); - } catch (IllegalAccessException e) { - LogLog.warn("Unable to instantiate jmdns class", e); - } catch (NoSuchMethodException e) { - LogLog.warn("Unable to access constructor", e); - } catch (InvocationTargetException e) { - LogLog.warn("Unable to call constructor", e); - } - return null; - } - - private Object buildServiceInfoVersion1(String zone, int port, String name, Map properties) { - //version 1 uses a hashtable - Hashtable hashtableProperties = new Hashtable(properties); - try { - Class[] args = new Class[6]; - args[0] = String.class; - args[1] = String.class; - args[2] = int.class; - args[3] = int.class; //weight (0) - args[4] = int.class; //priority (0) - args[5] = Hashtable.class; - Constructor constructor = serviceInfoClass.getConstructor(args); - Object[] values = new Object[6]; - values[0] = zone; - values[1] = name; - values[2] = new Integer(port); - values[3] = new Integer(0); - values[4] = new Integer(0); - values[5] = hashtableProperties; - Object result = constructor.newInstance(values); - LogLog.debug("created serviceinfo: " + result); - return result; - } catch (IllegalAccessException e) { - LogLog.warn("Unable to construct ServiceInfo instance", e); - } catch (NoSuchMethodException e) { - LogLog.warn("Unable to get ServiceInfo constructor", e); - } catch (InstantiationException e) { - LogLog.warn("Unable to construct ServiceInfo instance", e); - } catch (InvocationTargetException e) { - LogLog.warn("Unable to construct ServiceInfo instance", e); - } - return null; - } - - private Object buildServiceInfoVersion3(String zone, int port, String name, Map properties) { - try { - Class[] args = new Class[6]; - args[0] = String.class; //zone/type - args[1] = String.class; //display name - args[2] = int.class; //port - args[3] = int.class; //weight (0) - args[4] = int.class; //priority (0) - args[5] = Map.class; - Method serviceInfoCreateMethod = serviceInfoClass.getMethod("create", args); - Object[] values = new Object[6]; - values[0] = zone; - values[1] = name; - values[2] = new Integer(port); - values[3] = new Integer(0); - values[4] = new Integer(0); - values[5] = properties; - Object result = serviceInfoCreateMethod.invoke(null, values); - LogLog.debug("created serviceinfo: " + result); - return result; - } catch (IllegalAccessException e) { - LogLog.warn("Unable to invoke create method", e); - } catch (NoSuchMethodException e) { - LogLog.warn("Unable to find create method", e); - } catch (InvocationTargetException e) { - LogLog.warn("Unable to invoke create method", e); - } - return null; - } - - public void advertise() { - try { - Method method = jmDNSClass.getMethod("registerService", new Class[]{serviceInfoClass}); - method.invoke(jmDNS, new Object[]{serviceInfo}); - LogLog.debug("registered serviceInfo: " + serviceInfo); - } catch(IllegalAccessException e) { - LogLog.warn("Unable to invoke registerService method", e); - } catch(NoSuchMethodException e) { - LogLog.warn("No registerService method", e); - } catch(InvocationTargetException e) { - LogLog.warn("Unable to invoke registerService method", e); - } - } - - public void unadvertise() { - try { - Method method = jmDNSClass.getMethod("unregisterService", new Class[]{serviceInfoClass}); - method.invoke(jmDNS, new Object[]{serviceInfo}); - LogLog.debug("unregistered serviceInfo: " + serviceInfo); - } catch(IllegalAccessException e) { - LogLog.warn("Unable to invoke unregisterService method", e); - } catch(NoSuchMethodException e) { - LogLog.warn("No unregisterService method", e); - } catch(InvocationTargetException e) { - LogLog.warn("Unable to invoke unregisterService method", e); - } - } - - private static Object initializeJMDNS() { - try { - jmDNSClass = Class.forName("javax.jmdns.JmDNS"); - serviceInfoClass = Class.forName("javax.jmdns.ServiceInfo"); - } catch (ClassNotFoundException e) { - LogLog.warn("JmDNS or serviceInfo class not found", e); - } - - //if version 3 is available, use it to constuct a serviceInfo instance, otherwise support the version1 API - boolean isVersion3 = false; - try { - //create method is in version 3, not version 1 - jmDNSClass.getMethod("create", null); - isVersion3 = true; - } catch (NoSuchMethodException e) { - //no-op - } - - if (isVersion3) { - return createJmDNSVersion3(); - } else { - return createJmDNSVersion1(); - } - } - - public static Object getJMDNSInstance() { - return jmDNS; - } -} diff --git a/java/src/org/apache/log4j/net/package.html b/java/src/org/apache/log4j/net/package.html deleted file mode 100644 index 1d2e965..0000000 --- a/java/src/org/apache/log4j/net/package.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - -

Package for remote logging. - -


-
- -Last modified: Tue Mar 21 20:28:14 MET 2000 - - diff --git a/java/src/org/apache/log4j/nt/NTEventLogAppender.java b/java/src/org/apache/log4j/nt/NTEventLogAppender.java deleted file mode 100644 index 9f80add..0000000 --- a/java/src/org/apache/log4j/nt/NTEventLogAppender.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.nt; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.Layout; -import org.apache.log4j.TTCCLayout; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.spi.LoggingEvent; - - -/** - Append to the NT event log system. - -

WARNING This appender can only be installed and used on a - Windows system. - -

Do not forget to place NTEventLogAppender.dll, - NTEventLogAppender.amd64.dll, NTEventLogAppender.ia64.dll - or NTEventLogAppender.x86.dll as appropriate in a - directory that is on the PATH of the Windows system. Otherwise, you - will get a java.lang.UnsatisfiedLinkError. - - @author Chris Taylor - @author Jim Cakalic */ -public class NTEventLogAppender extends AppenderSkeleton { - private int _handle = 0; - - private String source = null; - private String server = null; - - - public NTEventLogAppender() { - this(null, null, null); - } - - public NTEventLogAppender(String source) { - this(null, source, null); - } - - public NTEventLogAppender(String server, String source) { - this(server, source, null); - } - - public NTEventLogAppender(Layout layout) { - this(null, null, layout); - } - - public NTEventLogAppender(String source, Layout layout) { - this(null, source, layout); - } - - public NTEventLogAppender(String server, String source, Layout layout) { - if (source == null) { - source = "Log4j"; - } - if (layout == null) { - this.layout = new TTCCLayout(); - } else { - this.layout = layout; - } - - try { - _handle = registerEventSource(server, source); - } catch (Exception e) { - e.printStackTrace(); - _handle = 0; - } - } - - public - void close() { - // unregister ... - } - - public - void activateOptions() { - if (source != null) { - try { - _handle = registerEventSource(server, source); - } catch (Exception e) { - LogLog.error("Could not register event source.", e); - _handle = 0; - } - } - } - - - public void append(LoggingEvent event) { - - StringBuffer sbuf = new StringBuffer(); - - sbuf.append(layout.format(event)); - if(layout.ignoresThrowable()) { - String[] s = event.getThrowableStrRep(); - if (s != null) { - int len = s.length; - for(int i = 0; i < len; i++) { - sbuf.append(s[i]); - } - } - } - // Normalize the log message level into the supported categories - int nt_category = event.getLevel().toInt(); - - // Anything above FATAL or below DEBUG is labeled as INFO. - //if (nt_category > FATAL || nt_category < DEBUG) { - // nt_category = INFO; - //} - reportEvent(_handle, sbuf.toString(), nt_category); - } - - - public - void finalize() { - deregisterEventSource(_handle); - _handle = 0; - } - - /** - The Source option which names the source of the event. The - current value of this constant is Source. - */ - public - void setSource(String source) { - this.source = source.trim(); - } - - public - String getSource() { - return source; - } - -/** - The NTEventLogAppender requires a layout. Hence, - this method always returns true. */ - public - boolean requiresLayout() { - return true; - } - - native private int registerEventSource(String server, String source); - native private void reportEvent(int handle, String message, int level); - native private void deregisterEventSource(int handle); - - static { - String[] archs; - try { - archs = new String[] { System.getProperty("os.arch")}; - } catch(SecurityException e) { - archs = new String[] { "amd64", "ia64", "x86"}; - } - boolean loaded = false; - for(int i = 0; i < archs.length; i++) { - try { - System.loadLibrary("NTEventLogAppender." + archs[i]); - loaded = true; - break; - } catch(java.lang.UnsatisfiedLinkError e) { - loaded = false; - } - } - if (!loaded) { - System.loadLibrary("NTEventLogAppender"); - } -} -} diff --git a/java/src/org/apache/log4j/nt/package.html b/java/src/org/apache/log4j/nt/package.html deleted file mode 100644 index a50567c..0000000 --- a/java/src/org/apache/log4j/nt/package.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - -

Package for NT event logging. - -


-
- -Last modified: Sat Apr 29 14:30:12 MDT 2000 - - diff --git a/java/src/org/apache/log4j/or/DefaultRenderer.java b/java/src/org/apache/log4j/or/DefaultRenderer.java deleted file mode 100644 index 7fe4ac6..0000000 --- a/java/src/org/apache/log4j/or/DefaultRenderer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.or; - -/** - The default Renderer renders objects by calling their - toString method. - - @author Ceki Gülcü - @since 1.0 */ -class DefaultRenderer implements ObjectRenderer { - - DefaultRenderer() { - } - - /** - Render the object passed as parameter by calling its - toString method. */ - public - String doRender(final Object o) { - try { - return o.toString(); - } catch(Exception ex) { - return ex.toString(); - } - } -} diff --git a/java/src/org/apache/log4j/or/ObjectRenderer.java b/java/src/org/apache/log4j/or/ObjectRenderer.java deleted file mode 100644 index 8ad9943..0000000 --- a/java/src/org/apache/log4j/or/ObjectRenderer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.or; - -/** - Implement this interface in order to render objects as strings. - - @author Ceki Gülcü - @since 1.0 */ -public interface ObjectRenderer { - - /** - Render the object passed as parameter as a String. - */ - public - String doRender(Object o); -} diff --git a/java/src/org/apache/log4j/or/RendererMap.java b/java/src/org/apache/log4j/or/RendererMap.java deleted file mode 100644 index f60bd5a..0000000 --- a/java/src/org/apache/log4j/or/RendererMap.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.or; - -import org.apache.log4j.spi.RendererSupport; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.helpers.Loader; -import org.apache.log4j.helpers.OptionConverter; -import java.util.Hashtable; - -/** - Map class objects to an {@link ObjectRenderer}. - - @author Ceki Gülcü - @since version 1.0 */ -public class RendererMap { - - Hashtable map; - - static ObjectRenderer defaultRenderer = new DefaultRenderer(); - - public - RendererMap() { - map = new Hashtable(); - } - - /** - Add a renderer to a hierarchy passed as parameter. - */ - static - public - void addRenderer(RendererSupport repository, String renderedClassName, - String renderingClassName) { - LogLog.debug("Rendering class: ["+renderingClassName+"], Rendered class: ["+ - renderedClassName+"]."); - ObjectRenderer renderer = (ObjectRenderer) - OptionConverter.instantiateByClassName(renderingClassName, - ObjectRenderer.class, - null); - if(renderer == null) { - LogLog.error("Could not instantiate renderer ["+renderingClassName+"]."); - return; - } else { - try { - Class renderedClass = Loader.loadClass(renderedClassName); - repository.setRenderer(renderedClass, renderer); - } catch(ClassNotFoundException e) { - LogLog.error("Could not find class ["+renderedClassName+"].", e); - } - } - } - - - /** - Find the appropriate renderer for the class type of the - o parameter. This is accomplished by calling the - {@link #get(Class)} method. Once a renderer is found, it is - applied on the object o and the result is returned - as a {@link String}. */ - public - String findAndRender(Object o) { - if(o == null) - return null; - else - return get(o.getClass()).doRender(o); - } - - - /** - Syntactic sugar method that calls {@link #get(Class)} with the - class of the object parameter. */ - public - ObjectRenderer get(Object o) { - if(o == null) - return null; - else - return get(o.getClass()); - } - - - /** - Search the parents of clazz for a renderer. The - renderer closest in the hierarchy will be returned. If no - renderers could be found, then the default renderer is returned. - -

The search first looks for a renderer configured for - clazz. If a renderer could not be found, then the - search continues by looking at all the interfaces implemented by - clazz including the super-interfaces of each - interface. If a renderer cannot be found, then the search looks - for a renderer defined for the parent (superclass) of - clazz. If that fails, then all the interfaces - implemented by the parent of clazz are searched and - so on. - -

For example, if A0, A1, A2 are classes and X0, X1, X2, Y0, Y1 - are interfaces where A2 extends A1 which in turn extends A0 and - similarly X2 extends X1 which extends X0 and Y1 extends Y0. Let - us also assume that A1 implements the Y0 interface and that A2 - implements the X2 interface. - -

The table below shows the results returned by the - get(A2.class) method depending on the renderers - added to the map. - -

- - -
Added renderersValue returned by get(A2.class)
A0Renderer - A0Renderer - -
A0Renderer, A1Renderer - A1Renderer - -
X0Renderer - X0Renderer - -
A1Renderer, X0Renderer - X0Renderer - -
- -

This search algorithm is not the most natural, although it is - particularly easy to implement. Future log4j versions - may implement a more intuitive search - algorithm. However, the present algorithm should be acceptable in - the vast majority of circumstances. - - */ - public - ObjectRenderer get(Class clazz) { - //System.out.println("\nget: "+clazz); - ObjectRenderer r = null; - for(Class c = clazz; c != null; c = c.getSuperclass()) { - //System.out.println("Searching for class: "+c); - r = (ObjectRenderer) map.get(c); - if(r != null) { - return r; - } - r = searchInterfaces(c); - if(r != null) - return r; - } - return defaultRenderer; - } - - ObjectRenderer searchInterfaces(Class c) { - //System.out.println("Searching interfaces of class: "+c); - - ObjectRenderer r = (ObjectRenderer) map.get(c); - if(r != null) { - return r; - } else { - Class[] ia = c.getInterfaces(); - for(int i = 0; i < ia.length; i++) { - r = searchInterfaces(ia[i]); - if(r != null) - return r; - } - } - return null; - } - - - public - ObjectRenderer getDefaultRenderer() { - return defaultRenderer; - } - - - public - void clear() { - map.clear(); - } - - /** - Register an {@link ObjectRenderer} for clazz. - */ - public - void put(Class clazz, ObjectRenderer or) { - map.put(clazz, or); - } -} diff --git a/java/src/org/apache/log4j/or/ThreadGroupRenderer.java b/java/src/org/apache/log4j/or/ThreadGroupRenderer.java deleted file mode 100644 index 026ff4f..0000000 --- a/java/src/org/apache/log4j/or/ThreadGroupRenderer.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.or; - -import org.apache.log4j.Layout; - - -/** - Render {@link ThreadGroup} objects in a format similar to the - information output by the {@link ThreadGroup#list} method. - @author Ceki Gülcü - @since 1.0 */ -public class ThreadGroupRenderer implements ObjectRenderer { - - public - ThreadGroupRenderer() { - } - - /** - Render a {@link ThreadGroup} object similar to the way that the - {@link ThreadGroup#list} method output information. - -

The output of a simple program consisting of one - main thread is: -

-     java.lang.ThreadGroup[name=main, maxpri=10]
-         Thread=[main,5,false]
-     
- -

The boolean value in thread information is the value returned - by {@link Thread#isDaemon}. - - */ - public - String doRender(Object o) { - if(o instanceof ThreadGroup) { - StringBuffer sbuf = new StringBuffer(); - ThreadGroup tg = (ThreadGroup) o; - sbuf.append("java.lang.ThreadGroup[name="); - sbuf.append(tg.getName()); - sbuf.append(", maxpri="); - sbuf.append(tg.getMaxPriority()); - sbuf.append("]"); - Thread[] t = new Thread[tg.activeCount()]; - tg.enumerate(t); - for(int i = 0; i < t.length; i++) { - sbuf.append(Layout.LINE_SEP); - sbuf.append(" Thread=["); - sbuf.append(t[i].getName()); - sbuf.append(","); - sbuf.append(t[i].getPriority()); - sbuf.append(","); - sbuf.append(t[i].isDaemon()); - sbuf.append("]"); - } - return sbuf.toString(); - } else { - try { - // this is the best we can do - return o.toString(); - } catch(Exception ex) { - return ex.toString(); - } - } - } -} diff --git a/java/src/org/apache/log4j/or/jms/MessageRenderer.java b/java/src/org/apache/log4j/or/jms/MessageRenderer.java deleted file mode 100644 index e3140cb..0000000 --- a/java/src/org/apache/log4j/or/jms/MessageRenderer.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.or.jms; - -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.or.ObjectRenderer; - -import javax.jms.Message; -import javax.jms.JMSException; -import javax.jms.DeliveryMode; - -/** - Render javax.jms.Message objects. - - @author Ceki Gülcü - @since 1.0 */ -public class MessageRenderer implements ObjectRenderer { - - public - MessageRenderer() { - } - - - /** - Render a {@link javax.jms.Message}. - */ - public - String doRender(Object o) { - if(o instanceof Message) { - StringBuffer sbuf = new StringBuffer(); - Message m = (Message) o; - try { - sbuf.append("DeliveryMode="); - switch(m.getJMSDeliveryMode()) { - case DeliveryMode.NON_PERSISTENT : - sbuf.append("NON_PERSISTENT"); - break; - case DeliveryMode.PERSISTENT : - sbuf.append("PERSISTENT"); - break; - default: sbuf.append("UNKNOWN"); - } - sbuf.append(", CorrelationID="); - sbuf.append(m.getJMSCorrelationID()); - - sbuf.append(", Destination="); - sbuf.append(m.getJMSDestination()); - - sbuf.append(", Expiration="); - sbuf.append(m.getJMSExpiration()); - - sbuf.append(", MessageID="); - sbuf.append(m.getJMSMessageID()); - - sbuf.append(", Priority="); - sbuf.append(m.getJMSPriority()); - - sbuf.append(", Redelivered="); - sbuf.append(m.getJMSRedelivered()); - - sbuf.append(", ReplyTo="); - sbuf.append(m.getJMSReplyTo()); - - sbuf.append(", Timestamp="); - sbuf.append(m.getJMSTimestamp()); - - sbuf.append(", Type="); - sbuf.append(m.getJMSType()); - - //Enumeration enum = m.getPropertyNames(); - //while(enum.hasMoreElements()) { - // String key = (String) enum.nextElement(); - // sbuf.append("; "+key+"="); - // sbuf.append(m.getStringProperty(key)); - //} - - } catch(JMSException e) { - LogLog.error("Could not parse Message.", e); - } - return sbuf.toString(); - } else { - return o.toString(); - } - } -} diff --git a/java/src/org/apache/log4j/or/jms/package.html b/java/src/org/apache/log4j/or/jms/package.html deleted file mode 100644 index d4db1c8..0000000 --- a/java/src/org/apache/log4j/or/jms/package.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - This package contains the MessageRenderer which renders objects of - type javax.jms.Message. - - \ No newline at end of file diff --git a/java/src/org/apache/log4j/or/package.html b/java/src/org/apache/log4j/or/package.html deleted file mode 100644 index 17fd176..0000000 --- a/java/src/org/apache/log4j/or/package.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - -org.apache.log4j.or package - - - - -

ObjectRenders are resposible for rendering messages depending on -their class type. - -


- diff --git a/java/src/org/apache/log4j/or/sax/AttributesRenderer.java b/java/src/org/apache/log4j/or/sax/AttributesRenderer.java deleted file mode 100644 index b5d088c..0000000 --- a/java/src/org/apache/log4j/or/sax/AttributesRenderer.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.or.sax; - -import org.apache.log4j.or.ObjectRenderer; - -import org.xml.sax.Attributes; - -/** - Render org.xml.sax.Attributes objects. - - @author Ceki Gülcü - @since 1.2 */ -public class AttributesRenderer implements ObjectRenderer { - - public - AttributesRenderer() { - } - - - /** - Render a {@link org.xml.sax.Attributes}. - */ - public - String doRender(Object o) { - if(o instanceof Attributes) { - StringBuffer sbuf = new StringBuffer(); - Attributes a = (Attributes) o; - int len = a.getLength(); - boolean first = true; - for(int i = 0; i < len; i++) { - if(first) { - first = false; - } else { - sbuf.append(", "); - } - sbuf.append(a.getQName(i)); - sbuf.append('='); - sbuf.append(a.getValue(i)); - } - return sbuf.toString(); - } else { - try { - return o.toString(); - } catch(Exception ex) { - return ex.toString(); - } - } - } -} - diff --git a/java/src/org/apache/log4j/or/sax/package.html b/java/src/org/apache/log4j/or/sax/package.html deleted file mode 100644 index a597141..0000000 --- a/java/src/org/apache/log4j/or/sax/package.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - This package contains the AttributesRenderer which renders object of - class org.xml.sax.Attributes. - - \ No newline at end of file diff --git a/java/src/org/apache/log4j/package.html b/java/src/org/apache/log4j/package.html deleted file mode 100644 index 3cf6e3e..0000000 --- a/java/src/org/apache/log4j/package.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - -

The main log4j package. - -


- diff --git a/java/src/org/apache/log4j/pattern/BridgePatternConverter.java b/java/src/org/apache/log4j/pattern/BridgePatternConverter.java deleted file mode 100644 index 96068d5..0000000 --- a/java/src/org/apache/log4j/pattern/BridgePatternConverter.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - - -/** - * The class implements the pre log4j 1.3 org.apache.log4j.helpers.PatternConverter - * contract by delegating to the log4j 1.3 pattern implementation. - * - * - * @author Curt Arnold - * - */ -public final class BridgePatternConverter - extends org.apache.log4j.helpers.PatternConverter { - /** - * Pattern converters. - */ - private LoggingEventPatternConverter[] patternConverters; - - /** - * Field widths and alignment corresponding to pattern converters. - */ - private FormattingInfo[] patternFields; - - /** - * Does pattern process exceptions. - */ - private boolean handlesExceptions; - - /** - * Create a new instance. - * @param pattern pattern, may not be null. - */ - public BridgePatternConverter( - final String pattern) { - next = null; - handlesExceptions = false; - - List converters = new ArrayList(); - List fields = new ArrayList(); - Map converterRegistry = null; - - PatternParser.parse( - pattern, converters, fields, converterRegistry, - PatternParser.getPatternLayoutRules()); - - patternConverters = new LoggingEventPatternConverter[converters.size()]; - patternFields = new FormattingInfo[converters.size()]; - - int i = 0; - Iterator converterIter = converters.iterator(); - Iterator fieldIter = fields.iterator(); - - while (converterIter.hasNext()) { - Object converter = converterIter.next(); - - if (converter instanceof LoggingEventPatternConverter) { - patternConverters[i] = (LoggingEventPatternConverter) converter; - handlesExceptions |= patternConverters[i].handlesThrowable(); - } else { - patternConverters[i] = - new org.apache.log4j.pattern.LiteralPatternConverter(""); - } - - if (fieldIter.hasNext()) { - patternFields[i] = (FormattingInfo) fieldIter.next(); - } else { - patternFields[i] = FormattingInfo.getDefault(); - } - - i++; - } - } - - /** - * {@inheritDoc} - */ - protected String convert(final LoggingEvent event) { - // - // code should be unreachable. - // - StringBuffer sbuf = new StringBuffer(); - format(sbuf, event); - - return sbuf.toString(); - } - - /** - Format event to string buffer. - @param sbuf string buffer to receive formatted event, may not be null. - @param e event to format, may not be null. - */ - public void format(final StringBuffer sbuf, final LoggingEvent e) { - for (int i = 0; i < patternConverters.length; i++) { - int startField = sbuf.length(); - patternConverters[i].format(e, sbuf); - patternFields[i].format(startField, sbuf); - } - } - - /** - * Will return false if any of the conversion specifiers in the pattern - * handles {@link Exception Exceptions}. - * @return true if the pattern formats any information from exceptions. - */ - public boolean ignoresThrowable() { - return !handlesExceptions; - } -} diff --git a/java/src/org/apache/log4j/pattern/BridgePatternParser.java b/java/src/org/apache/log4j/pattern/BridgePatternParser.java deleted file mode 100644 index 1c0d4e7..0000000 --- a/java/src/org/apache/log4j/pattern/BridgePatternParser.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -/** - * The class implements the pre log4j 1.3 org.apache.log4j.helpers.PatternConverter - * contract by delegating to the log4j 1.3 pattern implementation. - * - * - * @author Curt Arnold - * - */ -public final class BridgePatternParser - extends org.apache.log4j.helpers.PatternParser { - - - /** - * Create a new instance. - * @param conversionPattern pattern, may not be null. - */ - public BridgePatternParser( - final String conversionPattern) { - super(conversionPattern); - } - - /** - * Create new pattern converter. - * @return pattern converter. - */ - public org.apache.log4j.helpers.PatternConverter parse() { - return new BridgePatternConverter(pattern); - } -} diff --git a/java/src/org/apache/log4j/pattern/CachedDateFormat.java b/java/src/org/apache/log4j/pattern/CachedDateFormat.java deleted file mode 100644 index 9a468aa..0000000 --- a/java/src/org/apache/log4j/pattern/CachedDateFormat.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import java.text.DateFormat; -import java.text.FieldPosition; -import java.text.NumberFormat; -import java.text.ParsePosition; -import java.util.Date; -import java.util.TimeZone; - - -/** - * CachedDateFormat optimizes the performance of a wrapped - * DateFormat. The implementation is not thread-safe. - * If the millisecond pattern is not recognized, - * the class will only use the cache if the - * same value is requested. - * - */ -public final class CachedDateFormat extends DateFormat { - /** - * Serialization version. - */ - private static final long serialVersionUID = 1; - /** - * Constant used to represent that there was no change - * observed when changing the millisecond count. - */ - public static final int NO_MILLISECONDS = -2; - - /** - * Supported digit set. If the wrapped DateFormat uses - * a different unit set, the millisecond pattern - * will not be recognized and duplicate requests - * will use the cache. - */ - private static final String DIGITS = "0123456789"; - - /** - * Constant used to represent that there was an - * observed change, but was an expected change. - */ - public static final int UNRECOGNIZED_MILLISECONDS = -1; - - /** - * First magic number used to detect the millisecond position. - */ - private static final int MAGIC1 = 654; - - /** - * Expected representation of first magic number. - */ - private static final String MAGICSTRING1 = "654"; - - /** - * Second magic number used to detect the millisecond position. - */ - private static final int MAGIC2 = 987; - - /** - * Expected representation of second magic number. - */ - private static final String MAGICSTRING2 = "987"; - - /** - * Expected representation of 0 milliseconds. - */ - private static final String ZERO_STRING = "000"; - - /** - * Wrapped formatter. - */ - private final DateFormat formatter; - - /** - * Index of initial digit of millisecond pattern or - * UNRECOGNIZED_MILLISECONDS or NO_MILLISECONDS. - */ - private int millisecondStart; - - /** - * Integral second preceding the previous convered Date. - */ - private long slotBegin; - - /** - * Cache of previous conversion. - */ - private StringBuffer cache = new StringBuffer(50); - - /** - * Maximum validity period for the cache. - * Typically 1, use cache for duplicate requests only, or - * 1000, use cache for requests within the same integral second. - */ - private final int expiration; - - /** - * Date requested in previous conversion. - */ - private long previousTime; - - /** - * Scratch date object used to minimize date object creation. - */ - private final Date tmpDate = new Date(0); - - /** - * Creates a new CachedDateFormat object. - * @param dateFormat Date format, may not be null. - * @param expiration maximum cached range in milliseconds. - * If the dateFormat is known to be incompatible with the - * caching algorithm, use a value of 0 to totally disable - * caching or 1 to only use cache for duplicate requests. - */ - public CachedDateFormat(final DateFormat dateFormat, final int expiration) { - if (dateFormat == null) { - throw new IllegalArgumentException("dateFormat cannot be null"); - } - - if (expiration < 0) { - throw new IllegalArgumentException("expiration must be non-negative"); - } - - formatter = dateFormat; - this.expiration = expiration; - millisecondStart = 0; - - // - // set the previousTime so the cache will be invalid - // for the next request. - previousTime = Long.MIN_VALUE; - slotBegin = Long.MIN_VALUE; - } - - /** - * Finds start of millisecond field in formatted time. - * @param time long time, must be integral number of seconds - * @param formatted String corresponding formatted string - * @param formatter DateFormat date format - * @return int position in string of first digit of milliseconds, - * -1 indicates no millisecond field, -2 indicates unrecognized - * field (likely RelativeTimeDateFormat) - */ - public static int findMillisecondStart( - final long time, final String formatted, final DateFormat formatter) { - long slotBegin = (time / 1000) * 1000; - - if (slotBegin > time) { - slotBegin -= 1000; - } - - int millis = (int) (time - slotBegin); - - int magic = MAGIC1; - String magicString = MAGICSTRING1; - - if (millis == MAGIC1) { - magic = MAGIC2; - magicString = MAGICSTRING2; - } - - String plusMagic = formatter.format(new Date(slotBegin + magic)); - - /** - * If the string lengths differ then - * we can't use the cache except for duplicate requests. - */ - if (plusMagic.length() != formatted.length()) { - return UNRECOGNIZED_MILLISECONDS; - } else { - // find first difference between values - for (int i = 0; i < formatted.length(); i++) { - if (formatted.charAt(i) != plusMagic.charAt(i)) { - // - // determine the expected digits for the base time - StringBuffer formattedMillis = new StringBuffer("ABC"); - millisecondFormat(millis, formattedMillis, 0); - - String plusZero = formatter.format(new Date(slotBegin)); - - // If the next 3 characters match the magic - // string and the expected string - if ( - (plusZero.length() == formatted.length()) - && magicString.regionMatches( - 0, plusMagic, i, magicString.length()) - && formattedMillis.toString().regionMatches( - 0, formatted, i, magicString.length()) - && ZERO_STRING.regionMatches( - 0, plusZero, i, ZERO_STRING.length())) { - return i; - } else { - return UNRECOGNIZED_MILLISECONDS; - } - } - } - } - - return NO_MILLISECONDS; - } - - /** - * Formats a Date into a date/time string. - * - * @param date the date to format. - * @param sbuf the string buffer to write to. - * @param fieldPosition remains untouched. - * @return the formatted time string. - */ - public StringBuffer format( - Date date, StringBuffer sbuf, FieldPosition fieldPosition) { - format(date.getTime(), sbuf); - - return sbuf; - } - - /** - * Formats a millisecond count into a date/time string. - * - * @param now Number of milliseconds after midnight 1 Jan 1970 GMT. - * @param buf the string buffer to write to. - * @return the formatted time string. - */ - public StringBuffer format(long now, StringBuffer buf) { - // - // If the current requested time is identical to the previously - // requested time, then append the cache contents. - // - if (now == previousTime) { - buf.append(cache); - - return buf; - } - - // - // If millisecond pattern was not unrecognized - // (that is if it was found or milliseconds did not appear) - // - if (millisecondStart != UNRECOGNIZED_MILLISECONDS && - // Check if the cache is still valid. - // If the requested time is within the same integral second - // as the last request and a shorter expiration was not requested. - (now < (slotBegin + expiration)) && (now >= slotBegin) - && (now < (slotBegin + 1000L))) { - // - // if there was a millisecond field then update it - // - if (millisecondStart >= 0) { - millisecondFormat((int) (now - slotBegin), cache, millisecondStart); - } - - // - // update the previously requested time - // (the slot begin should be unchanged) - previousTime = now; - buf.append(cache); - - return buf; - } - - // - // could not use previous value. - // Call underlying formatter to format date. - cache.setLength(0); - tmpDate.setTime(now); - cache.append(formatter.format(tmpDate)); - buf.append(cache); - previousTime = now; - slotBegin = (previousTime / 1000) * 1000; - - if (slotBegin > previousTime) { - slotBegin -= 1000; - } - - // - // if the milliseconds field was previous found - // then reevaluate in case it moved. - // - if (millisecondStart >= 0) { - millisecondStart = - findMillisecondStart(now, cache.toString(), formatter); - } - - return buf; - } - - /** - * Formats a count of milliseconds (0-999) into a numeric representation. - * @param millis Millisecond coun between 0 and 999. - * @param buf String buffer, may not be null. - * @param offset Starting position in buffer, the length of the - * buffer must be at least offset + 3. - */ - private static void millisecondFormat( - final int millis, final StringBuffer buf, final int offset) { - buf.setCharAt(offset, DIGITS.charAt(millis / 100)); - buf.setCharAt(offset + 1, DIGITS.charAt((millis / 10) % 10)); - buf.setCharAt(offset + 2, DIGITS.charAt(millis % 10)); - } - - /** - * Set timezone. - * - * Setting the timezone using getCalendar().setTimeZone() - * will likely cause caching to misbehave. - * @param timeZone TimeZone new timezone - */ - public void setTimeZone(final TimeZone timeZone) { - formatter.setTimeZone(timeZone); - previousTime = Long.MIN_VALUE; - slotBegin = Long.MIN_VALUE; - } - - /** - * This method is delegated to the formatter which most - * likely returns null. - * @param s string representation of date. - * @param pos field position, unused. - * @return parsed date, likely null. - */ - public Date parse(String s, ParsePosition pos) { - return formatter.parse(s, pos); - } - - /** - * Gets number formatter. - * - * @return NumberFormat number formatter - */ - public NumberFormat getNumberFormat() { - return formatter.getNumberFormat(); - } - - /** - * Gets maximum cache validity for the specified SimpleDateTime - * conversion pattern. - * @param pattern conversion pattern, may not be null. - * @return Duration in milliseconds from an integral second - * that the cache will return consistent results. - */ - public static int getMaximumCacheValidity(final String pattern) { - // - // If there are more "S" in the pattern than just one "SSS" then - // (for example, "HH:mm:ss,SSS SSS"), then set the expiration to - // one millisecond which should only perform duplicate request caching. - // - int firstS = pattern.indexOf('S'); - - if ((firstS >= 0) && (firstS != pattern.lastIndexOf("SSS"))) { - return 1; - } - - return 1000; - } -} diff --git a/java/src/org/apache/log4j/pattern/ClassNamePatternConverter.java b/java/src/org/apache/log4j/pattern/ClassNamePatternConverter.java deleted file mode 100644 index 7a24916..0000000 --- a/java/src/org/apache/log4j/pattern/ClassNamePatternConverter.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Formats the class name of the site of the logging request. - * - * @author Ceki Gülcü - */ -public final class ClassNamePatternConverter extends NamePatternConverter { - /** - * Private constructor. - * @param options options, may be null. - */ - private ClassNamePatternConverter( - final String[] options) { - super("Class Name", "class name", options); - } - - /** - * Gets an instance of ClassNamePatternConverter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static ClassNamePatternConverter newInstance( - final String[] options) { - return new ClassNamePatternConverter(options); - } - - /** - * Format a logging event. - * @param event event to format. - * @param toAppendTo string buffer to which class name will be appended. - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - final int initialLength = toAppendTo.length(); - LocationInfo li = event.getLocationInformation(); - - if (li == null) { - toAppendTo.append(LocationInfo.NA); - } else { - toAppendTo.append(li.getClassName()); - } - - abbreviate(initialLength, toAppendTo); - } -} diff --git a/java/src/org/apache/log4j/pattern/DatePatternConverter.java b/java/src/org/apache/log4j/pattern/DatePatternConverter.java deleted file mode 100644 index 7162fe7..0000000 --- a/java/src/org/apache/log4j/pattern/DatePatternConverter.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.spi.LoggingEvent; - -import java.text.SimpleDateFormat; -import java.text.DateFormat; -import java.text.FieldPosition; -import java.text.ParsePosition; -import java.util.Date; -import java.util.TimeZone; - - -/** - * Convert and format the event's date in a StringBuffer. - * - * @author Ceki Gülcü - */ -public final class DatePatternConverter extends LoggingEventPatternConverter { - /** - * ABSOLUTE string literal. - */ - private static final String ABSOLUTE_FORMAT = "ABSOLUTE"; - /** - * SimpleTimePattern for ABSOLUTE. - */ - private static final String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS"; - - - /** - * DATE string literal. - */ - private static final String DATE_AND_TIME_FORMAT = "DATE"; - /** - * SimpleTimePattern for DATE. - */ - private static final String DATE_AND_TIME_PATTERN = "dd MMM yyyy HH:mm:ss,SSS"; - - /** - * ISO8601 string literal. - */ - private static final String ISO8601_FORMAT = "ISO8601"; - /** - * SimpleTimePattern for ISO8601. - */ - private static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS"; - /** - * Date format. - */ - private final CachedDateFormat df; - - /** - * This class wraps a DateFormat and forces the time zone to the - * default time zone before each format and parse request. - */ - private static class DefaultZoneDateFormat extends DateFormat { - /** - * Serialization version ID. - */ - private static final long serialVersionUID = 1; - /** - * Wrapped instance of DateFormat. - */ - private final DateFormat dateFormat; - - /** - * Construct new instance. - * @param format format, may not be null. - */ - public DefaultZoneDateFormat(final DateFormat format) { - dateFormat = format; - } - - /** - * @{inheritDoc} - */ - public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { - dateFormat.setTimeZone(TimeZone.getDefault()); - return dateFormat.format(date, toAppendTo, fieldPosition); - } - - /** - * @{inheritDoc} - */ - public Date parse(String source, ParsePosition pos) { - dateFormat.setTimeZone(TimeZone.getDefault()); - return dateFormat.parse(source, pos); - } - } - - /** - * Private constructor. - * @param options options, may be null. - */ - private DatePatternConverter(final String[] options) { - super("Date", "date"); - - String patternOption; - - if ((options == null) || (options.length == 0)) { - // the branch could be optimized, but here we are making explicit - // that null values for patternOption are allowed. - patternOption = null; - } else { - patternOption = options[0]; - } - - String pattern; - - if ( - (patternOption == null) - || patternOption.equalsIgnoreCase(ISO8601_FORMAT)) { - pattern = ISO8601_PATTERN; - } else if (patternOption.equalsIgnoreCase(ABSOLUTE_FORMAT)) { - pattern = ABSOLUTE_TIME_PATTERN; - } else if (patternOption.equalsIgnoreCase(DATE_AND_TIME_FORMAT)) { - pattern = DATE_AND_TIME_PATTERN; - } else { - pattern = patternOption; - } - - int maximumCacheValidity = 1000; - DateFormat simpleFormat = null; - - try { - simpleFormat = new SimpleDateFormat(pattern); - maximumCacheValidity = CachedDateFormat.getMaximumCacheValidity(pattern); - } catch (IllegalArgumentException e) { - LogLog.warn( - "Could not instantiate SimpleDateFormat with pattern " - + patternOption, e); - - // default to the ISO8601 format - simpleFormat = new SimpleDateFormat(ISO8601_PATTERN); - } - - // if the option list contains a TZ option, then set it. - if ((options != null) && (options.length > 1)) { - TimeZone tz = TimeZone.getTimeZone((String) options[1]); - simpleFormat.setTimeZone(tz); - } else { - simpleFormat = new DefaultZoneDateFormat(simpleFormat); - } - - df = new CachedDateFormat(simpleFormat, maximumCacheValidity); - } - - /** - * Obtains an instance of pattern converter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static DatePatternConverter newInstance( - final String[] options) { - return new DatePatternConverter(options); - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer output) { - synchronized(this) { - df.format(event.timeStamp, output); - } - } - - /** - * {@inheritDoc} - */ - public void format(final Object obj, final StringBuffer output) { - if (obj instanceof Date) { - format((Date) obj, output); - } - - super.format(obj, output); - } - - /** - * Append formatted date to string buffer. - * @param date date - * @param toAppendTo buffer to which formatted date is appended. - */ - public void format(final Date date, final StringBuffer toAppendTo) { - synchronized(this) { - df.format(date.getTime(), toAppendTo); - } - } -} diff --git a/java/src/org/apache/log4j/pattern/FileDatePatternConverter.java b/java/src/org/apache/log4j/pattern/FileDatePatternConverter.java deleted file mode 100644 index 1e08826..0000000 --- a/java/src/org/apache/log4j/pattern/FileDatePatternConverter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -/** - * Formats an date by delegating to DatePatternConverter. The default - * date pattern for a %d specifier in a file name is different than - * the %d pattern in pattern layout. - * - * @author Curt Arnold - */ -public final class FileDatePatternConverter { - /** - * Private constructor. - */ - private FileDatePatternConverter() { - } - - /** - * Obtains an instance of pattern converter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static PatternConverter newInstance( - final String[] options) { - if ((options == null) || (options.length == 0)) { - return DatePatternConverter.newInstance( - new String[] { - "yyyy-MM-dd" - }); - } - - return DatePatternConverter.newInstance(options); - } -} diff --git a/java/src/org/apache/log4j/pattern/FileLocationPatternConverter.java b/java/src/org/apache/log4j/pattern/FileLocationPatternConverter.java deleted file mode 100644 index 14eb31c..0000000 --- a/java/src/org/apache/log4j/pattern/FileLocationPatternConverter.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Return the event's line location information in a StringBuffer. - * - * @author Ceki Gülcü - */ -public final class FileLocationPatternConverter - extends LoggingEventPatternConverter { - /** - * Singleton. - */ - private static final FileLocationPatternConverter INSTANCE = - new FileLocationPatternConverter(); - - /** - * Private constructor. - */ - private FileLocationPatternConverter() { - super("File Location", "file"); - } - - /** - * Obtains an instance of pattern converter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static FileLocationPatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer output) { - LocationInfo locationInfo = event.getLocationInformation(); - - if (locationInfo != null) { - output.append(locationInfo.getFileName()); - } - } -} diff --git a/java/src/org/apache/log4j/pattern/FormattingInfo.java b/java/src/org/apache/log4j/pattern/FormattingInfo.java deleted file mode 100644 index 8a1a12e..0000000 --- a/java/src/org/apache/log4j/pattern/FormattingInfo.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - - -/** - * Modifies the output of a pattern converter for a specified minimum - * and maximum width and alignment. - * - * - * @author Jim Cakalic - * @author Ceki Gülcü - * @author Curt Arnold - * - */ -public final class FormattingInfo { - /** - * Array of spaces. - */ - private static final char[] SPACES = - new char[] { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; - - /** - * Default instance. - */ - private static final FormattingInfo DEFAULT = - new FormattingInfo(false, 0, Integer.MAX_VALUE); - - /** - * Minimum length. - */ - private final int minLength; - - /** - * Maximum length. - */ - private final int maxLength; - - /** - * Alignment. - */ - private final boolean leftAlign; - - /** - * Creates new instance. - * @param leftAlign left align if true. - * @param minLength minimum length. - * @param maxLength maximum length. - */ - public FormattingInfo( - final boolean leftAlign, final int minLength, final int maxLength) { - this.leftAlign = leftAlign; - this.minLength = minLength; - this.maxLength = maxLength; - } - - /** - * Gets default instance. - * @return default instance. - */ - public static FormattingInfo getDefault() { - return DEFAULT; - } - - /** - * Determine if left aligned. - * @return true if left aligned. - */ - public boolean isLeftAligned() { - return leftAlign; - } - - /** - * Get minimum length. - * @return minimum length. - */ - public int getMinLength() { - return minLength; - } - - /** - * Get maximum length. - * @return maximum length. - */ - public int getMaxLength() { - return maxLength; - } - - /** - * Adjust the content of the buffer based on the specified lengths and alignment. - * - * @param fieldStart start of field in buffer. - * @param buffer buffer to be modified. - */ - public void format(final int fieldStart, final StringBuffer buffer) { - final int rawLength = buffer.length() - fieldStart; - - if (rawLength > maxLength) { - buffer.delete(fieldStart, buffer.length() - maxLength); - } else if (rawLength < minLength) { - if (leftAlign) { - final int fieldEnd = buffer.length(); - buffer.setLength(fieldStart + minLength); - - for (int i = fieldEnd; i < buffer.length(); i++) { - buffer.setCharAt(i, ' '); - } - } else { - int padLength = minLength - rawLength; - - for (; padLength > 8; padLength -= 8) { - buffer.insert(fieldStart, SPACES); - } - - buffer.insert(fieldStart, SPACES, 0, padLength); - } - } - } -} diff --git a/java/src/org/apache/log4j/pattern/FullLocationPatternConverter.java b/java/src/org/apache/log4j/pattern/FullLocationPatternConverter.java deleted file mode 100644 index 066016c..0000000 --- a/java/src/org/apache/log4j/pattern/FullLocationPatternConverter.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Format the event's line location information. - * - * @author Ceki Gülcü - */ -public final class FullLocationPatternConverter - extends LoggingEventPatternConverter { - /** - * Singleton. - */ - private static final FullLocationPatternConverter INSTANCE = - new FullLocationPatternConverter(); - - /** - * Private constructor. - */ - private FullLocationPatternConverter() { - super("Full Location", "fullLocation"); - } - - /** - * Obtains an instance of pattern converter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static FullLocationPatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer output) { - LocationInfo locationInfo = event.getLocationInformation(); - - if (locationInfo != null) { - output.append(locationInfo.fullInfo); - } - } -} diff --git a/java/src/org/apache/log4j/pattern/IntegerPatternConverter.java b/java/src/org/apache/log4j/pattern/IntegerPatternConverter.java deleted file mode 100644 index f50feeb..0000000 --- a/java/src/org/apache/log4j/pattern/IntegerPatternConverter.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import java.util.Date; - - -/** - * Formats an integer. - * - * @author Curt Arnold - */ -public final class IntegerPatternConverter extends PatternConverter { - /** - * Singleton. - */ - private static final IntegerPatternConverter INSTANCE = - new IntegerPatternConverter(); - - /** - * Private constructor. - */ - private IntegerPatternConverter() { - super("Integer", "integer"); - } - - /** - * Obtains an instance of pattern converter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static IntegerPatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(Object obj, final StringBuffer toAppendTo) { - if (obj instanceof Integer) { - toAppendTo.append(obj.toString()); - } - - if (obj instanceof Date) { - toAppendTo.append(Long.toString(((Date) obj).getTime())); - } - } -} diff --git a/java/src/org/apache/log4j/pattern/LevelPatternConverter.java b/java/src/org/apache/log4j/pattern/LevelPatternConverter.java deleted file mode 100644 index 3dbc6e6..0000000 --- a/java/src/org/apache/log4j/pattern/LevelPatternConverter.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.Level; -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Return the event's level in a StringBuffer. - * - * @author Ceki Gülcü - */ -public final class LevelPatternConverter extends LoggingEventPatternConverter { - - /** - * Integer severity for Level.TRACE. - */ - private static final int TRACE_INT = 5000; - /** - * Singleton. - */ - private static final LevelPatternConverter INSTANCE = - new LevelPatternConverter(); - - /** - * Private constructor. - */ - private LevelPatternConverter() { - super("Level", "level"); - } - - /** - * Obtains an instance of pattern converter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static LevelPatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer output) { - output.append(event.getLevel().toString()); - } - - /** - * {@inheritDoc} - */ - public String getStyleClass(Object e) { - if (e instanceof LoggingEvent) { - int lint = ((LoggingEvent) e).getLevel().toInt(); - - switch (lint) { - case TRACE_INT: - return "level trace"; - - case Level.DEBUG_INT: - return "level debug"; - - case Level.INFO_INT: - return "level info"; - - case Level.WARN_INT: - return "level warn"; - - case Level.ERROR_INT: - return "level error"; - - case Level.FATAL_INT: - return "level fatal"; - - default: - return "level " + ((LoggingEvent) e).getLevel().toString(); - } - } - - return "level"; - } -} diff --git a/java/src/org/apache/log4j/pattern/LineLocationPatternConverter.java b/java/src/org/apache/log4j/pattern/LineLocationPatternConverter.java deleted file mode 100644 index 0a9dfd3..0000000 --- a/java/src/org/apache/log4j/pattern/LineLocationPatternConverter.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Return the event's line location information in a StringBuffer. - * - * @author Ceki Gülcü - */ -public final class LineLocationPatternConverter - extends LoggingEventPatternConverter { - /** - * Singleton. - */ - private static final LineLocationPatternConverter INSTANCE = - new LineLocationPatternConverter(); - - /** - * Private constructor. - */ - private LineLocationPatternConverter() { - super("Line", "line"); - } - - /** - * Obtains an instance of pattern converter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static LineLocationPatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer output) { - LocationInfo locationInfo = event.getLocationInformation(); - - if (locationInfo != null) { - output.append(locationInfo.getLineNumber()); - } - } -} diff --git a/java/src/org/apache/log4j/pattern/LineSeparatorPatternConverter.java b/java/src/org/apache/log4j/pattern/LineSeparatorPatternConverter.java deleted file mode 100644 index a859870..0000000 --- a/java/src/org/apache/log4j/pattern/LineSeparatorPatternConverter.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.Layout; -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Formats a line separator. - * - * @author Ceki Gülcü - */ -public final class LineSeparatorPatternConverter - extends LoggingEventPatternConverter { - /** - * Singleton. - */ - private static final LineSeparatorPatternConverter INSTANCE = - new LineSeparatorPatternConverter(); - - /** - * Line separator. - */ - private final String lineSep; - - /** - * Private constructor. - */ - private LineSeparatorPatternConverter() { - super("Line Sep", "lineSep"); - lineSep = Layout.LINE_SEP; - } - - /** - * Obtains an instance of pattern converter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static LineSeparatorPatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(LoggingEvent event, final StringBuffer toAppendTo) { - toAppendTo.append(lineSep); - } - - /** - * {@inheritDoc} - */ - public void format(final Object obj, final StringBuffer toAppendTo) { - toAppendTo.append(lineSep); - } -} diff --git a/java/src/org/apache/log4j/pattern/LiteralPatternConverter.java b/java/src/org/apache/log4j/pattern/LiteralPatternConverter.java deleted file mode 100644 index b88b6f7..0000000 --- a/java/src/org/apache/log4j/pattern/LiteralPatternConverter.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Formats a string literal. - * - * @author Curt Arnold - * - */ -public final class LiteralPatternConverter extends LoggingEventPatternConverter { - /** - * String literal. - */ - private final String literal; - - /** - * Create a new instance. - * @param literal string literal. - */ - public LiteralPatternConverter(final String literal) { - super("Literal", "literal"); - this.literal = literal; - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - toAppendTo.append(literal); - } - - /** - * {@inheritDoc} - */ - public void format(final Object obj, final StringBuffer toAppendTo) { - toAppendTo.append(literal); - } -} diff --git a/java/src/org/apache/log4j/pattern/LogEvent.java b/java/src/org/apache/log4j/pattern/LogEvent.java deleted file mode 100644 index 24518ec..0000000 --- a/java/src/org/apache/log4j/pattern/LogEvent.java +++ /dev/null @@ -1,600 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.Category; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.MDC; -import org.apache.log4j.NDC; -import org.apache.log4j.Priority; -import org.apache.log4j.helpers.Loader; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.spi.LoggerRepository; -import org.apache.log4j.spi.RendererSupport; -import org.apache.log4j.spi.ThrowableInformation; - -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.lang.reflect.Method; -import java.util.Collections; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; -import java.util.Set; - -// Contributors: Nelson Minar -// Wolf Siberski -// Anders Kristensen - -/** - * This class is a copy of o.a.l.spi.LoggingEvent from - * log4j 1.2.15 which has been renamed to keep - * the same overall class name length to allow a - * serialization written with a prior instance of o.a.l.spi.LoggingEvent - * to be deserialized with this class just by mangling the class name - * in the byte stream. - * - */ -public class LogEvent implements java.io.Serializable { - - private static long startTime = System.currentTimeMillis(); - - /** Fully qualified name of the calling category class. */ - transient public final String fqnOfCategoryClass; - - /** - * The category of the logging event. This field is not serialized - * for performance reasons. - * - *

It is set by the LoggingEvent constructor or set by a remote - * entity after deserialization. - * - * @deprecated This field will be marked as private or be completely - * removed in future releases. Please do not use it. - * */ - transient private Category logger; - - /** - *

The category (logger) name. - * - * @deprecated This field will be marked as private in future - * releases. Please do not access it directly. Use the {@link - * #getLoggerName} method instead. - - * */ - final public String categoryName; - - /** - * Level of logging event. Level cannot be serializable because it - * is a flyweight. Due to its special seralization it cannot be - * declared final either. - * - *

This field should not be accessed directly. You shoud use the - * {@link #getLevel} method instead. - * - * @deprecated This field will be marked as private in future - * releases. Please do not access it directly. Use the {@link - * #getLevel} method instead. - * */ - transient public Priority level; - - /** The nested diagnostic context (NDC) of logging event. */ - private String ndc; - - /** The mapped diagnostic context (MDC) of logging event. */ - private Hashtable mdcCopy; - - - /** Have we tried to do an NDC lookup? If we did, there is no need - * to do it again. Note that its value is always false when - * serialized. Thus, a receiving SocketNode will never use it's own - * (incorrect) NDC. See also writeObject method. */ - private boolean ndcLookupRequired = true; - - - /** Have we tried to do an MDC lookup? If we did, there is no need - * to do it again. Note that its value is always false when - * serialized. See also the getMDC and getMDCCopy methods. */ - private boolean mdcCopyLookupRequired = true; - - /** The application supplied message of logging event. */ - transient private Object message; - - /** The application supplied message rendered through the log4j - objet rendering mechanism.*/ - private String renderedMessage; - - /** The name of thread in which this logging event was generated. */ - private String threadName; - - - /** This - variable contains information about this event's throwable - */ - private ThrowableInformation throwableInfo; - - /** The number of milliseconds elapsed from 1/1/1970 until logging event - was created. */ - public final long timeStamp; - /** Location information for the caller. */ - private LocationInfo locationInfo; - - // Serialization - static final long serialVersionUID = -868428216207166145L; - - static final Integer[] PARAM_ARRAY = new Integer[1]; - static final String TO_LEVEL = "toLevel"; - static final Class[] TO_LEVEL_PARAMS = new Class[] {int.class}; - static final Hashtable methodCache = new Hashtable(3); // use a tiny table - - /** - Instantiate a LoggingEvent from the supplied parameters. - -

Except {@link #timeStamp} all the other fields of - LoggingEvent are filled when actually needed. -

- @param logger The logger generating this event. - @param level The level of this event. - @param message The message of this event. - @param throwable The throwable of this event. */ - public LogEvent(String fqnOfCategoryClass, Category logger, - Priority level, Object message, Throwable throwable) { - this.fqnOfCategoryClass = fqnOfCategoryClass; - this.logger = logger; - this.categoryName = logger.getName(); - this.level = level; - this.message = message; - if(throwable != null) { - this.throwableInfo = new ThrowableInformation(throwable); - } - timeStamp = System.currentTimeMillis(); - } - - /** - Instantiate a LoggingEvent from the supplied parameters. - -

Except {@link #timeStamp} all the other fields of - LoggingEvent are filled when actually needed. -

- @param logger The logger generating this event. - @param timeStamp the timestamp of this logging event - @param level The level of this event. - @param message The message of this event. - @param throwable The throwable of this event. */ - public LogEvent(String fqnOfCategoryClass, Category logger, - long timeStamp, Priority level, Object message, - Throwable throwable) { - this.fqnOfCategoryClass = fqnOfCategoryClass; - this.logger = logger; - this.categoryName = logger.getName(); - this.level = level; - this.message = message; - if(throwable != null) { - this.throwableInfo = new ThrowableInformation(throwable); - } - - this.timeStamp = timeStamp; - } - - /** - Create new instance. - @since 1.2.15 - @param fqnOfCategoryClass Fully qualified class name - of Logger implementation. - @param logger The logger generating this event. - @param timeStamp the timestamp of this logging event - @param level The level of this event. - @param message The message of this event. - @param threadName thread name - @param throwable The throwable of this event. - @param ndc Nested diagnostic context - @param info Location info - @param properties MDC properties - */ - public LogEvent(final String fqnOfCategoryClass, - final Logger logger, - final long timeStamp, - final Level level, - final Object message, - final String threadName, - final ThrowableInformation throwable, - final String ndc, - final LocationInfo info, - final java.util.Map properties) { - super(); - this.fqnOfCategoryClass = fqnOfCategoryClass; - this.logger = logger; - if (logger != null) { - categoryName = logger.getName(); - } else { - categoryName = null; - } - this.level = level; - this.message = message; - if(throwable != null) { - this.throwableInfo = throwable; - } - - this.timeStamp = timeStamp; - this.threadName = threadName; - ndcLookupRequired = false; - this.ndc = ndc; - this.locationInfo = info; - mdcCopyLookupRequired = false; - if (properties != null) { - mdcCopy = new java.util.Hashtable(properties); - } - } - - /** - Set the location information for this logging event. The collected - information is cached for future use. - */ - public LocationInfo getLocationInformation() { - if(locationInfo == null) { - locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass); - } - return locationInfo; - } - - /** - * Return the level of this event. Use this form instead of directly - * accessing the level field. */ - public Level getLevel() { - return (Level) level; - } - - /** - * Return the name of the logger. Use this form instead of directly - * accessing the categoryName field. - */ - public String getLoggerName() { - return categoryName; - } - - /** - Return the message for this logging event. - -

Before serialization, the returned object is the message - passed by the user to generate the logging event. After - serialization, the returned value equals the String form of the - message possibly after object rendering. - - @since 1.1 */ - public - Object getMessage() { - if(message != null) { - return message; - } else { - return getRenderedMessage(); - } - } - - /** - * This method returns the NDC for this event. It will return the - * correct content even if the event was generated in a different - * thread or even on a different machine. The {@link NDC#get} method - * should never be called directly. */ - public - String getNDC() { - if(ndcLookupRequired) { - ndcLookupRequired = false; - ndc = NDC.get(); - } - return ndc; - } - - - /** - Returns the the context corresponding to the key - parameter. If there is a local MDC copy, possibly because we are - in a logging server or running inside AsyncAppender, then we - search for the key in MDC copy, if a value is found it is - returned. Otherwise, if the search in MDC copy returns a null - result, then the current thread's MDC is used. - -

Note that both the local MDC copy and the current - thread's MDC are searched. - - */ - public - Object getMDC(String key) { - Object r; - // Note the mdcCopy is used if it exists. Otherwise we use the MDC - // that is associated with the thread. - if(mdcCopy != null) { - r = mdcCopy.get(key); - if(r != null) { - return r; - } - } - return MDC.get(key); - } - - /** - Obtain a copy of this thread's MDC prior to serialization or - asynchronous logging. - */ - public - void getMDCCopy() { - if(mdcCopyLookupRequired) { - mdcCopyLookupRequired = false; - // the clone call is required for asynchronous logging. - // See also bug #5932. - Hashtable t = (Hashtable) MDC.getContext(); - if(t != null) { - mdcCopy = (Hashtable) t.clone(); - } - } - } - - public - String getRenderedMessage() { - if(renderedMessage == null && message != null) { - if(message instanceof String) - renderedMessage = (String) message; - else { - LoggerRepository repository = logger.getLoggerRepository(); - - if(repository instanceof RendererSupport) { - RendererSupport rs = (RendererSupport) repository; - renderedMessage= rs.getRendererMap().findAndRender(message); - } else { - renderedMessage = message.toString(); - } - } - } - return renderedMessage; - } - - /** - Returns the time when the application started, in milliseconds - elapsed since 01.01.1970. */ - public static long getStartTime() { - return startTime; - } - - public - String getThreadName() { - if(threadName == null) - threadName = (Thread.currentThread()).getName(); - return threadName; - } - - /** - Returns the throwable information contained within this - event. May be null if there is no such information. - -

Note that the {@link Throwable} object contained within a - {@link ThrowableInformation} does not survive serialization. - - @since 1.1 */ - public - ThrowableInformation getThrowableInformation() { - return throwableInfo; - } - - /** - Return this event's throwable's string[] representaion. - */ - public - String[] getThrowableStrRep() { - - if(throwableInfo == null) - return null; - else - return throwableInfo.getThrowableStrRep(); - } - - - private - void readLevel(ObjectInputStream ois) - throws java.io.IOException, ClassNotFoundException { - - int p = ois.readInt(); - try { - String className = (String) ois.readObject(); - if(className == null) { - level = Level.toLevel(p); - } else { - Method m = (Method) methodCache.get(className); - if(m == null) { - Class clazz = Loader.loadClass(className); - // Note that we use Class.getDeclaredMethod instead of - // Class.getMethod. This assumes that the Level subclass - // implements the toLevel(int) method which is a - // requirement. Actually, it does not make sense for Level - // subclasses NOT to implement this method. Also note that - // only Level can be subclassed and not Priority. - m = clazz.getDeclaredMethod(TO_LEVEL, TO_LEVEL_PARAMS); - methodCache.put(className, m); - } - PARAM_ARRAY[0] = new Integer(p); - level = (Level) m.invoke(null, PARAM_ARRAY); - } - } catch(Exception e) { - LogLog.warn("Level deserialization failed, reverting to default.", e); - level = Level.toLevel(p); - } - } - - private void readObject(ObjectInputStream ois) - throws java.io.IOException, ClassNotFoundException { - ois.defaultReadObject(); - readLevel(ois); - - // Make sure that no location info is available to Layouts - if(locationInfo == null) - locationInfo = new LocationInfo(null, null); - } - - private - void writeObject(ObjectOutputStream oos) throws java.io.IOException { - // Aside from returning the current thread name the wgetThreadName - // method sets the threadName variable. - this.getThreadName(); - - // This sets the renders the message in case it wasn't up to now. - this.getRenderedMessage(); - - // This call has a side effect of setting this.ndc and - // setting ndcLookupRequired to false if not already false. - this.getNDC(); - - // This call has a side effect of setting this.mdcCopy and - // setting mdcLookupRequired to false if not already false. - this.getMDCCopy(); - - // This sets the throwable sting representation of the event throwable. - this.getThrowableStrRep(); - - oos.defaultWriteObject(); - - // serialize this event's level - writeLevel(oos); - } - - private - void writeLevel(ObjectOutputStream oos) throws java.io.IOException { - - oos.writeInt(level.toInt()); - - Class clazz = level.getClass(); - if(clazz == Level.class) { - oos.writeObject(null); - } else { - // writing directly the Class object would be nicer, except that - // serialized a Class object can not be read back by JDK - // 1.1.x. We have to resort to this hack instead. - oos.writeObject(clazz.getName()); - } - } - - /** - * Set value for MDC property. - * This adds the specified MDC property to the event. - * Access to the MDC is not synchronized, so this - * method should only be called when it is known that - * no other threads are accessing the MDC. - * @since 1.2.15 - * @param propName - * @param propValue - */ - public final void setProperty(final String propName, - final String propValue) { - if (mdcCopy == null) { - getMDCCopy(); - } - if (mdcCopy == null) { - mdcCopy = new Hashtable(); - } - mdcCopy.put(propName, propValue); - } - - /** - * Return a property for this event. The return value can be null. - * - * Equivalent to getMDC(String) in log4j 1.2. Provided - * for compatibility with log4j 1.3. - * - * @param key property name - * @return property value or null if property not set - * @since 1.2.15 - */ - public final String getProperty(final String key) { - Object value = getMDC(key); - String retval = null; - if (value != null) { - retval = value.toString(); - } - return retval; - } - - /** - * Check for the existence of location information without creating it - * (a byproduct of calling getLocationInformation). - * @return true if location information has been extracted. - * @since 1.2.15 - */ - public final boolean locationInformationExists() { - return (locationInfo != null); - } - - /** - * Getter for the event's time stamp. The time stamp is calculated starting - * from 1970-01-01 GMT. - * @return timestamp - * - * @since 1.2.15 - */ - public final long getTimeStamp() { - return timeStamp; - } - - /** - * Returns the set of the key values in the properties - * for the event. - * - * The returned set is unmodifiable by the caller. - * - * Provided for compatibility with log4j 1.3 - * - * @return Set an unmodifiable set of the property keys. - * @since 1.2.15 - */ - public Set getPropertyKeySet() { - return getProperties().keySet(); - } - - /** - * Returns the set of properties - * for the event. - * - * The returned set is unmodifiable by the caller. - * - * Provided for compatibility with log4j 1.3 - * - * @return Set an unmodifiable map of the properties. - * @since 1.2.15 - */ - public Map getProperties() { - getMDCCopy(); - Map properties; - if (mdcCopy == null) { - properties = new HashMap(); - } else { - properties = mdcCopy; - } - return Collections.unmodifiableMap(properties); - } - - /** - * Get the fully qualified name of the calling logger sub-class/wrapper. - * Provided for compatibility with log4j 1.3 - * @return fully qualified class name, may be null. - * @since 1.2.15 - */ - public String getFQNOfLoggerClass() { - return fqnOfCategoryClass; - } - - - -} diff --git a/java/src/org/apache/log4j/pattern/LoggerPatternConverter.java b/java/src/org/apache/log4j/pattern/LoggerPatternConverter.java deleted file mode 100644 index 7052a4e..0000000 --- a/java/src/org/apache/log4j/pattern/LoggerPatternConverter.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Formats a logger name. - * - * @author Ceki Gülcü - * - */ -public final class LoggerPatternConverter extends NamePatternConverter { - /** - * Singleton. - */ - private static final LoggerPatternConverter INSTANCE = - new LoggerPatternConverter(null); - - /** - * Private constructor. - * @param options options, may be null. - */ - private LoggerPatternConverter(final String[] options) { - super("Logger", "logger", options); - } - - /** - * Obtains an instance of pattern converter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static LoggerPatternConverter newInstance( - final String[] options) { - if ((options == null) || (options.length == 0)) { - return INSTANCE; - } - - return new LoggerPatternConverter(options); - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - final int initialLength = toAppendTo.length(); - toAppendTo.append(event.getLoggerName()); - abbreviate(initialLength, toAppendTo); - } -} diff --git a/java/src/org/apache/log4j/pattern/LoggingEventPatternConverter.java b/java/src/org/apache/log4j/pattern/LoggingEventPatternConverter.java deleted file mode 100644 index d0fd4f4..0000000 --- a/java/src/org/apache/log4j/pattern/LoggingEventPatternConverter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; - - -/** - * LoggingEventPatternConverter is a base class for pattern converters - * that can format information from instances of LoggingEvent. - * - * @author Curt Arnold - * - */ -public abstract class LoggingEventPatternConverter extends PatternConverter { - /** - * Constructs an instance of LoggingEventPatternConverter. - * @param name name of converter. - * @param style CSS style for output. - */ - protected LoggingEventPatternConverter( - final String name, final String style) { - super(name, style); - } - - /** - * Formats an event into a string buffer. - * @param event event to format, may not be null. - * @param toAppendTo string buffer to which the formatted event will be appended. May not be null. - */ - public abstract void format( - final LoggingEvent event, final StringBuffer toAppendTo); - - /** - * {@inheritDoc} - */ - public void format(final Object obj, final StringBuffer output) { - if (obj instanceof LoggingEvent) { - format((LoggingEvent) obj, output); - } - } - - /** - * Normally pattern converters are not meant to handle Exceptions although - * few pattern converters might. - * - * By examining the return values for this method, the containing layout will - * determine whether it handles throwables or not. - - * @return true if this PatternConverter handles throwables - */ - public boolean handlesThrowable() { - return false; - } -} diff --git a/java/src/org/apache/log4j/pattern/MessagePatternConverter.java b/java/src/org/apache/log4j/pattern/MessagePatternConverter.java deleted file mode 100644 index c29f64a..0000000 --- a/java/src/org/apache/log4j/pattern/MessagePatternConverter.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Return the event's rendered message in a StringBuffer. - * - * @author Ceki Gülcü - */ -public final class MessagePatternConverter extends LoggingEventPatternConverter { - /** - * Singleton. - */ - private static final MessagePatternConverter INSTANCE = - new MessagePatternConverter(); - - /** - * Private constructor. - */ - private MessagePatternConverter() { - super("Message", "message"); - } - - /** - * Obtains an instance of pattern converter. - * @param options options, may be null. - * @return instance of pattern converter. - */ - public static MessagePatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - toAppendTo.append(event.getRenderedMessage()); - } -} diff --git a/java/src/org/apache/log4j/pattern/MethodLocationPatternConverter.java b/java/src/org/apache/log4j/pattern/MethodLocationPatternConverter.java deleted file mode 100644 index 4d1b533..0000000 --- a/java/src/org/apache/log4j/pattern/MethodLocationPatternConverter.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Return the event's line location information in a StringBuffer. - * - * @author Ceki Gülcü - */ -public final class MethodLocationPatternConverter - extends LoggingEventPatternConverter { - /** - * Singleton. - */ - private static final MethodLocationPatternConverter INSTANCE = - new MethodLocationPatternConverter(); - - /** - * Private constructor. - */ - private MethodLocationPatternConverter() { - super("Method", "method"); - } - - /** - * Obtains an instance of MethodLocationPatternConverter. - * @param options options, may be null. - * @return instance of MethodLocationPatternConverter. - */ - public static MethodLocationPatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - LocationInfo locationInfo = event.getLocationInformation(); - - if (locationInfo != null) { - toAppendTo.append(locationInfo.getMethodName()); - } - } -} diff --git a/java/src/org/apache/log4j/pattern/NDCPatternConverter.java b/java/src/org/apache/log4j/pattern/NDCPatternConverter.java deleted file mode 100644 index 9788cb1..0000000 --- a/java/src/org/apache/log4j/pattern/NDCPatternConverter.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Return the event's NDC in a StringBuffer. - * - * @author Ceki Gülcü - */ -public final class NDCPatternConverter extends LoggingEventPatternConverter { - /** - * Singleton. - */ - private static final NDCPatternConverter INSTANCE = - new NDCPatternConverter(); - - /** - * Private constructor. - */ - private NDCPatternConverter() { - super("NDC", "ndc"); - } - - /** - * Obtains an instance of NDCPatternConverter. - * @param options options, may be null. - * @return instance of NDCPatternConverter. - */ - public static NDCPatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - toAppendTo.append(event.getNDC()); - } -} diff --git a/java/src/org/apache/log4j/pattern/NameAbbreviator.java b/java/src/org/apache/log4j/pattern/NameAbbreviator.java deleted file mode 100644 index b7e89a0..0000000 --- a/java/src/org/apache/log4j/pattern/NameAbbreviator.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import java.util.ArrayList; -import java.util.List; - - -/** - * NameAbbreviator generates abbreviated logger and class names. - * - */ -public abstract class NameAbbreviator { - /** - * Default (no abbreviation) abbreviator. - */ - private static final NameAbbreviator DEFAULT = new NOPAbbreviator(); - - /** - * Gets an abbreviator. - * - * For example, "%logger{2}" will output only 2 elements of the logger name, - * %logger{-2} will drop 2 elements from the logger name, - * "%logger{1.}" will output only the first character of the non-final elements in the name, - * "%logger{1~.2~} will output the first character of the first element, two characters of - * the second and subsequent elements and will use a tilde to indicate abbreviated characters. - * - * @param pattern abbreviation pattern. - * @return abbreviator, will not be null. - */ - public static NameAbbreviator getAbbreviator(final String pattern) { - if (pattern.length() > 0) { - // if pattern is just spaces and numbers then - // use MaxElementAbbreviator - String trimmed = pattern.trim(); - - if (trimmed.length() == 0) { - return DEFAULT; - } - - int i = 0; - if (trimmed.length() > 0) { - if (trimmed.charAt(0) == '-') { - i++; - } - for (; - (i < trimmed.length()) && - (trimmed.charAt(i) >= '0') && - (trimmed.charAt(i) <= '9'); - i++) { - } - } - - - // - // if all blanks and digits - // - if (i == trimmed.length()) { - int elements = Integer.parseInt(trimmed); - if (elements >= 0) { - return new MaxElementAbbreviator(elements); - } else { - return new DropElementAbbreviator(-elements); - } - } - - ArrayList fragments = new ArrayList(5); - char ellipsis; - int charCount; - int pos = 0; - - while ((pos < trimmed.length()) && (pos >= 0)) { - int ellipsisPos = pos; - - if (trimmed.charAt(pos) == '*') { - charCount = Integer.MAX_VALUE; - ellipsisPos++; - } else { - if ((trimmed.charAt(pos) >= '0') && (trimmed.charAt(pos) <= '9')) { - charCount = trimmed.charAt(pos) - '0'; - ellipsisPos++; - } else { - charCount = 0; - } - } - - ellipsis = '\0'; - - if (ellipsisPos < trimmed.length()) { - ellipsis = trimmed.charAt(ellipsisPos); - - if (ellipsis == '.') { - ellipsis = '\0'; - } - } - - fragments.add(new PatternAbbreviatorFragment(charCount, ellipsis)); - pos = trimmed.indexOf(".", pos); - - if (pos == -1) { - break; - } - - pos++; - } - - return new PatternAbbreviator(fragments); - } - - // - // no matching abbreviation, return defaultAbbreviator - // - return DEFAULT; - } - - /** - * Gets default abbreviator. - * - * @return default abbreviator. - */ - public static NameAbbreviator getDefaultAbbreviator() { - return DEFAULT; - } - - /** - * Abbreviates a name in a StringBuffer. - * - * @param nameStart starting position of name in buf. - * @param buf buffer, may not be null. - */ - public abstract void abbreviate(final int nameStart, final StringBuffer buf); - - /** - * Abbreviator that simply appends full name to buffer. - */ - private static class NOPAbbreviator extends NameAbbreviator { - /** - * Constructor. - */ - public NOPAbbreviator() { - } - - /** - * {@inheritDoc} - */ - public void abbreviate(final int nameStart, final StringBuffer buf) { - } - } - - /** - * Abbreviator that drops starting path elements. - */ - private static class MaxElementAbbreviator extends NameAbbreviator { - /** - * Maximum number of path elements to output. - */ - private final int count; - - /** - * Create new instance. - * @param count maximum number of path elements to output. - */ - public MaxElementAbbreviator(final int count) { - this.count = count; - } - - /** - * Abbreviate name. - * @param buf buffer to append abbreviation. - * @param nameStart start of name to abbreviate. - */ - public void abbreviate(final int nameStart, final StringBuffer buf) { - // We substract 1 from 'len' when assigning to 'end' to avoid out of - // bounds exception in return r.substring(end+1, len). This can happen if - // precision is 1 and the category name ends with a dot. - int end = buf.length() - 1; - - String bufString = buf.toString(); - for (int i = count; i > 0; i--) { - end = bufString.lastIndexOf(".", end - 1); - - if ((end == -1) || (end < nameStart)) { - return; - } - } - - buf.delete(nameStart, end + 1); - } - } - - /** - * Abbreviator that drops starting path elements. - */ - private static class DropElementAbbreviator extends NameAbbreviator { - /** - * Maximum number of path elements to output. - */ - private final int count; - - /** - * Create new instance. - * @param count maximum number of path elements to output. - */ - public DropElementAbbreviator(final int count) { - this.count = count; - } - - /** - * Abbreviate name. - * @param buf buffer to append abbreviation. - * @param nameStart start of name to abbreviate. - */ - public void abbreviate(final int nameStart, final StringBuffer buf) { - int i = count; - for(int pos = buf.indexOf(".", nameStart); - pos != -1; - pos = buf.indexOf(".", pos + 1)) { - if(--i == 0) { - buf.delete(nameStart, pos + 1); - break; - } - } - } - } - - - /** - * Fragment of an pattern abbreviator. - * - */ - private static class PatternAbbreviatorFragment { - /** - * Count of initial characters of element to output. - */ - private final int charCount; - - /** - * Character used to represent dropped characters. - * '\0' indicates no representation of dropped characters. - */ - private final char ellipsis; - - /** - * Creates a PatternAbbreviatorFragment. - * @param charCount number of initial characters to preserve. - * @param ellipsis character to represent elimination of characters, - * '\0' if no ellipsis is desired. - */ - public PatternAbbreviatorFragment( - final int charCount, final char ellipsis) { - this.charCount = charCount; - this.ellipsis = ellipsis; - } - - /** - * Abbreviate element of name. - * @param buf buffer to receive element. - * @param startPos starting index of name element. - * @return starting index of next element. - */ - public int abbreviate(final StringBuffer buf, final int startPos) { - int nextDot = buf.toString().indexOf(".", startPos); - - if (nextDot != -1) { - if ((nextDot - startPos) > charCount) { - buf.delete(startPos + charCount, nextDot); - nextDot = startPos + charCount; - - if (ellipsis != '\0') { - buf.insert(nextDot, ellipsis); - nextDot++; - } - } - - nextDot++; - } - - return nextDot; - } - } - - /** - * Pattern abbreviator. - * - * - */ - private static class PatternAbbreviator extends NameAbbreviator { - /** - * Element abbreviation patterns. - */ - private final PatternAbbreviatorFragment[] fragments; - - /** - * Create PatternAbbreviator. - * - * @param fragments element abbreviation patterns. - */ - public PatternAbbreviator(List fragments) { - if (fragments.size() == 0) { - throw new IllegalArgumentException( - "fragments must have at least one element"); - } - - this.fragments = new PatternAbbreviatorFragment[fragments.size()]; - fragments.toArray(this.fragments); - } - - /** - * Abbreviate name. - * @param buf buffer that abbreviated name is appended. - * @param nameStart start of name. - */ - public void abbreviate(final int nameStart, final StringBuffer buf) { - // - // all non-terminal patterns are executed once - // - int pos = nameStart; - - for (int i = 0; (i < (fragments.length - 1)) && (pos < buf.length()); - i++) { - pos = fragments[i].abbreviate(buf, pos); - } - - // - // last pattern in executed repeatedly - // - PatternAbbreviatorFragment terminalFragment = - fragments[fragments.length - 1]; - - while ((pos < buf.length()) && (pos >= 0)) { - pos = terminalFragment.abbreviate(buf, pos); - } - } - } -} diff --git a/java/src/org/apache/log4j/pattern/NamePatternConverter.java b/java/src/org/apache/log4j/pattern/NamePatternConverter.java deleted file mode 100644 index fbdd999..0000000 --- a/java/src/org/apache/log4j/pattern/NamePatternConverter.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - - -/** - * - * Base class for other pattern converters which can return only parts of their name. - * - * @author Ceki Gülcü - * @author Curt Arnold - * - */ -public abstract class NamePatternConverter - extends LoggingEventPatternConverter { - /** - * Abbreviator. - */ - private final NameAbbreviator abbreviator; - - /** - * Constructor. - * @param name name of converter. - * @param style style name for associated output. - * @param options options, may be null, first element will be interpreted as an abbreviation pattern. - */ - protected NamePatternConverter( - final String name, final String style, final String[] options) { - super(name, style); - - if ((options != null) && (options.length > 0)) { - abbreviator = NameAbbreviator.getAbbreviator(options[0]); - } else { - abbreviator = NameAbbreviator.getDefaultAbbreviator(); - } - } - - /** - * Abbreviate name in string buffer. - * @param nameStart starting position of name to abbreviate. - * @param buf string buffer containing name. - */ - protected final void abbreviate(final int nameStart, final StringBuffer buf) { - abbreviator.abbreviate(nameStart, buf); - } -} diff --git a/java/src/org/apache/log4j/pattern/PatternConverter.java b/java/src/org/apache/log4j/pattern/PatternConverter.java deleted file mode 100644 index 21fb7cd..0000000 --- a/java/src/org/apache/log4j/pattern/PatternConverter.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - - -/** - -

PatternConverter is an abstract class that provides the - formatting functionality that derived classes need. - -

Conversion specifiers in a conversion patterns are parsed to - individual PatternConverters. Each of which is responsible for - converting an object in a converter specific manner. - - @author James P. Cakalic - @author Ceki Gülcü - @author Chris Nokes - @author Curt Arnold - - */ -public abstract class PatternConverter { - /** - * Converter name. - */ - private final String name; - - /** - * Converter style name. - */ - private final String style; - - /** - * Create a new pattern converter. - * @param name name for pattern converter. - * @param style CSS style for formatted output. - */ - protected PatternConverter(final String name, final String style) { - this.name = name; - this.style = style; - } - - /** - * Formats an object into a string buffer. - * @param obj event to format, may not be null. - * @param toAppendTo string buffer to which the formatted event will be appended. May not be null. - */ - public abstract void format(final Object obj, final StringBuffer toAppendTo); - - /** - * This method returns the name of the conversion pattern. - * - * The name can be useful to certain Layouts such as HTMLLayout. - * - * @return the name of the conversion pattern - */ - public final String getName() { - return name; - } - - /** - * This method returns the CSS style class that should be applied to - * the LoggingEvent passed as parameter, which can be null. - * - * This information is currently used only by HTMLLayout. - * - * @param e null values are accepted - * @return the name of the conversion pattern - */ - public String getStyleClass(Object e) { - return style; - } -} diff --git a/java/src/org/apache/log4j/pattern/PatternParser.java b/java/src/org/apache/log4j/pattern/PatternParser.java deleted file mode 100644 index 5d19387..0000000 --- a/java/src/org/apache/log4j/pattern/PatternParser.java +++ /dev/null @@ -1,683 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.helpers.Loader; -import org.apache.log4j.helpers.LogLog; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -// Contributors: Nelson Minar <(nelson@monkey.org> -// Igor E. Poteryaev -// Reinhard Deschler - -/** - * Most of the work of the {@link org.apache.log4j.EnhancedPatternLayout} class - * is delegated to the PatternParser class. - *

It is this class that parses conversion patterns and creates - * a chained list of {@link PatternConverter PatternConverters}. - * - * @author James P. Cakalic - * @author Ceki Gülcü - * @author Anders Kristensen - * @author Paul Smith - * @author Curt Arnold - * -*/ -public final class PatternParser { - /** - * Escape character for format specifier. - */ - private static final char ESCAPE_CHAR = '%'; - - /** - * Literal state. - */ - private static final int LITERAL_STATE = 0; - - /** - * In converter name state. - */ - private static final int CONVERTER_STATE = 1; - - /** - * Dot state. - */ - private static final int DOT_STATE = 3; - - /** - * Min state. - */ - private static final int MIN_STATE = 4; - - /** - * Max state. - */ - private static final int MAX_STATE = 5; - - /** - * Standard format specifiers for EnhancedPatternLayout. - */ - private static final Map PATTERN_LAYOUT_RULES; - - /** - * Standard format specifiers for rolling file appenders. - */ - private static final Map FILENAME_PATTERN_RULES; - - static { - // We set the global rules in the static initializer of PatternParser class - Map rules = new HashMap(17); - rules.put("c", LoggerPatternConverter.class); - rules.put("logger", LoggerPatternConverter.class); - - rules.put("C", ClassNamePatternConverter.class); - rules.put("class", ClassNamePatternConverter.class); - - rules.put("d", DatePatternConverter.class); - rules.put("date", DatePatternConverter.class); - - rules.put("F", FileLocationPatternConverter.class); - rules.put("file", FileLocationPatternConverter.class); - - rules.put("l", FullLocationPatternConverter.class); - - rules.put("L", LineLocationPatternConverter.class); - rules.put("line", LineLocationPatternConverter.class); - - rules.put("m", MessagePatternConverter.class); - rules.put("message", MessagePatternConverter.class); - - rules.put("n", LineSeparatorPatternConverter.class); - - rules.put("M", MethodLocationPatternConverter.class); - rules.put("method", MethodLocationPatternConverter.class); - - rules.put("p", LevelPatternConverter.class); - rules.put("level", LevelPatternConverter.class); - - rules.put("r", RelativeTimePatternConverter.class); - rules.put("relative", RelativeTimePatternConverter.class); - - rules.put("t", ThreadPatternConverter.class); - rules.put("thread", ThreadPatternConverter.class); - - rules.put("x", NDCPatternConverter.class); - rules.put("ndc", NDCPatternConverter.class); - - rules.put("X", PropertiesPatternConverter.class); - rules.put("properties", PropertiesPatternConverter.class); - - rules.put("sn", SequenceNumberPatternConverter.class); - rules.put("sequenceNumber", SequenceNumberPatternConverter.class); - - rules.put("throwable", ThrowableInformationPatternConverter.class); - PATTERN_LAYOUT_RULES = new ReadOnlyMap(rules); - - Map fnameRules = new HashMap(4); - fnameRules.put("d", FileDatePatternConverter.class); - fnameRules.put("date", FileDatePatternConverter.class); - fnameRules.put("i", IntegerPatternConverter.class); - fnameRules.put("index", IntegerPatternConverter.class); - - FILENAME_PATTERN_RULES = new ReadOnlyMap(fnameRules); - } - - /** - * Private constructor. - */ - private PatternParser() { - } - - /** - * Get standard format specifiers for EnhancedPatternLayout. - * @return read-only map of format converter classes keyed by format specifier strings. - */ - public static Map getPatternLayoutRules() { - return PATTERN_LAYOUT_RULES; - } - - /** - * Get standard format specifiers for rolling file appender file specification. - * @return read-only map of format converter classes keyed by format specifier strings. - */ - public static Map getFileNamePatternRules() { - return FILENAME_PATTERN_RULES; - } - - /** Extract the converter identifier found at position i. - * - * After this function returns, the variable i will point to the - * first char after the end of the converter identifier. - * - * If i points to a char which is not a character acceptable at the - * start of a unicode identifier, the value null is returned. - * - * @param lastChar last processed character. - * @param pattern format string. - * @param i current index into pattern format. - * @param convBuf buffer to receive conversion specifier. - * @param currentLiteral literal to be output in case format specifier in unrecognized. - * @return position in pattern after converter. - */ - private static int extractConverter( - char lastChar, final String pattern, int i, final StringBuffer convBuf, - final StringBuffer currentLiteral) { - convBuf.setLength(0); - - // When this method is called, lastChar points to the first character of the - // conversion word. For example: - // For "%hello" lastChar = 'h' - // For "%-5hello" lastChar = 'h' - //System.out.println("lastchar is "+lastChar); - if (!Character.isUnicodeIdentifierStart(lastChar)) { - return i; - } - - convBuf.append(lastChar); - - while ( - (i < pattern.length()) - && Character.isUnicodeIdentifierPart(pattern.charAt(i))) { - convBuf.append(pattern.charAt(i)); - currentLiteral.append(pattern.charAt(i)); - - //System.out.println("conv buffer is now ["+convBuf+"]."); - i++; - } - - return i; - } - - /** - * Extract options. - * @param pattern conversion pattern. - * @param i start of options. - * @param options array to receive extracted options - * @return position in pattern after options. - */ - private static int extractOptions(String pattern, int i, List options) { - while ((i < pattern.length()) && (pattern.charAt(i) == '{')) { - int end = pattern.indexOf('}', i); - - if (end == -1) { - break; - } - - String r = pattern.substring(i + 1, end); - options.add(r); - i = end + 1; - } - - return i; - } - - /** - * Parse a format specifier. - * @param pattern pattern to parse. - * @param patternConverters list to receive pattern converters. - * @param formattingInfos list to receive field specifiers corresponding to pattern converters. - * @param converterRegistry map of user-supported pattern converters keyed by format specifier, may be null. - * @param rules map of stock pattern converters keyed by format specifier. - */ - public static void parse( - final String pattern, final List patternConverters, - final List formattingInfos, final Map converterRegistry, final Map rules) { - if (pattern == null) { - throw new NullPointerException("pattern"); - } - - StringBuffer currentLiteral = new StringBuffer(32); - - int patternLength = pattern.length(); - int state = LITERAL_STATE; - char c; - int i = 0; - FormattingInfo formattingInfo = FormattingInfo.getDefault(); - - while (i < patternLength) { - c = pattern.charAt(i++); - - switch (state) { - case LITERAL_STATE: - - // In literal state, the last char is always a literal. - if (i == patternLength) { - currentLiteral.append(c); - - continue; - } - - if (c == ESCAPE_CHAR) { - // peek at the next char. - switch (pattern.charAt(i)) { - case ESCAPE_CHAR: - currentLiteral.append(c); - i++; // move pointer - - break; - - default: - - if (currentLiteral.length() != 0) { - patternConverters.add( - new LiteralPatternConverter(currentLiteral.toString())); - formattingInfos.add(FormattingInfo.getDefault()); - } - - currentLiteral.setLength(0); - currentLiteral.append(c); // append % - state = CONVERTER_STATE; - formattingInfo = FormattingInfo.getDefault(); - } - } else { - currentLiteral.append(c); - } - - break; - - case CONVERTER_STATE: - currentLiteral.append(c); - - switch (c) { - case '-': - formattingInfo = - new FormattingInfo( - true, formattingInfo.getMinLength(), - formattingInfo.getMaxLength()); - - break; - - case '.': - state = DOT_STATE; - - break; - - default: - - if ((c >= '0') && (c <= '9')) { - formattingInfo = - new FormattingInfo( - formattingInfo.isLeftAligned(), c - '0', - formattingInfo.getMaxLength()); - state = MIN_STATE; - } else { - i = finalizeConverter( - c, pattern, i, currentLiteral, formattingInfo, - converterRegistry, rules, patternConverters, formattingInfos); - - // Next pattern is assumed to be a literal. - state = LITERAL_STATE; - formattingInfo = FormattingInfo.getDefault(); - currentLiteral.setLength(0); - } - } // switch - - break; - - case MIN_STATE: - currentLiteral.append(c); - - if ((c >= '0') && (c <= '9')) { - formattingInfo = - new FormattingInfo( - formattingInfo.isLeftAligned(), - (formattingInfo.getMinLength() * 10) + (c - '0'), - formattingInfo.getMaxLength()); - } else if (c == '.') { - state = DOT_STATE; - } else { - i = finalizeConverter( - c, pattern, i, currentLiteral, formattingInfo, - converterRegistry, rules, patternConverters, formattingInfos); - state = LITERAL_STATE; - formattingInfo = FormattingInfo.getDefault(); - currentLiteral.setLength(0); - } - - break; - - case DOT_STATE: - currentLiteral.append(c); - - if ((c >= '0') && (c <= '9')) { - formattingInfo = - new FormattingInfo( - formattingInfo.isLeftAligned(), formattingInfo.getMinLength(), - c - '0'); - state = MAX_STATE; - } else { - LogLog.error( - "Error occured in position " + i - + ".\n Was expecting digit, instead got char \"" + c + "\"."); - - state = LITERAL_STATE; - } - - break; - - case MAX_STATE: - currentLiteral.append(c); - - if ((c >= '0') && (c <= '9')) { - formattingInfo = - new FormattingInfo( - formattingInfo.isLeftAligned(), formattingInfo.getMinLength(), - (formattingInfo.getMaxLength() * 10) + (c - '0')); - } else { - i = finalizeConverter( - c, pattern, i, currentLiteral, formattingInfo, - converterRegistry, rules, patternConverters, formattingInfos); - state = LITERAL_STATE; - formattingInfo = FormattingInfo.getDefault(); - currentLiteral.setLength(0); - } - - break; - } // switch - } - - // while - if (currentLiteral.length() != 0) { - patternConverters.add( - new LiteralPatternConverter(currentLiteral.toString())); - formattingInfos.add(FormattingInfo.getDefault()); - } - } - - /** - * Creates a new PatternConverter. - * - * - * @param converterId converterId. - * @param currentLiteral literal to be used if converter is unrecognized or following converter - * if converterId contains extra characters. - * @param converterRegistry map of user-supported pattern converters keyed by format specifier, may be null. - * @param rules map of stock pattern converters keyed by format specifier. - * @param options converter options. - * @return converter or null. - */ - private static PatternConverter createConverter( - final String converterId, final StringBuffer currentLiteral, - final Map converterRegistry, final Map rules, final List options) { - String converterName = converterId; - Object converterObj = null; - - for (int i = converterId.length(); (i > 0) && (converterObj == null); - i--) { - converterName = converterName.substring(0, i); - - if (converterRegistry != null) { - converterObj = converterRegistry.get(converterName); - } - - if ((converterObj == null) && (rules != null)) { - converterObj = rules.get(converterName); - } - } - - if (converterObj == null) { - LogLog.error("Unrecognized format specifier [" + converterId + "]"); - - return null; - } - - Class converterClass = null; - - if (converterObj instanceof Class) { - converterClass = (Class) converterObj; - } else { - if (converterObj instanceof String) { - try { - converterClass = Loader.loadClass((String) converterObj); - } catch (ClassNotFoundException ex) { - LogLog.warn( - "Class for conversion pattern %" + converterName + " not found", - ex); - - return null; - } - } else { - LogLog.warn( - "Bad map entry for conversion pattern %" + converterName + "."); - - return null; - } - } - - try { - Method factory = - converterClass.getMethod( - "newInstance", - new Class[] { - Class.forName("[Ljava.lang.String;") - }); - String[] optionsArray = new String[options.size()]; - optionsArray = (String[]) options.toArray(optionsArray); - - Object newObj = - factory.invoke(null, new Object[] { optionsArray }); - - if (newObj instanceof PatternConverter) { - currentLiteral.delete( - 0, - currentLiteral.length() - - (converterId.length() - converterName.length())); - - return (PatternConverter) newObj; - } else { - LogLog.warn( - "Class " + converterClass.getName() - + " does not extend PatternConverter."); - } - } catch (Exception ex) { - LogLog.error("Error creating converter for " + converterId, ex); - - try { - // - // try default constructor - PatternConverter pc = (PatternConverter) converterClass.newInstance(); - currentLiteral.delete( - 0, - currentLiteral.length() - - (converterId.length() - converterName.length())); - - return pc; - } catch (Exception ex2) { - LogLog.error("Error creating converter for " + converterId, ex2); - } - } - - return null; - } - - /** - * Processes a format specifier sequence. - * - * @param c initial character of format specifier. - * @param pattern conversion pattern - * @param i current position in conversion pattern. - * @param currentLiteral current literal. - * @param formattingInfo current field specifier. - * @param converterRegistry map of user-provided pattern converters keyed by format specifier, may be null. - * @param rules map of stock pattern converters keyed by format specifier. - * @param patternConverters list to receive parsed pattern converter. - * @param formattingInfos list to receive corresponding field specifier. - * @return position after format specifier sequence. - */ - private static int finalizeConverter( - char c, String pattern, int i, - final StringBuffer currentLiteral, final FormattingInfo formattingInfo, - final Map converterRegistry, final Map rules, final List patternConverters, - final List formattingInfos) { - StringBuffer convBuf = new StringBuffer(); - i = extractConverter(c, pattern, i, convBuf, currentLiteral); - - String converterId = convBuf.toString(); - - List options = new ArrayList(); - i = extractOptions(pattern, i, options); - - PatternConverter pc = - createConverter( - converterId, currentLiteral, converterRegistry, rules, options); - - if (pc == null) { - StringBuffer msg; - - if ((converterId == null) || (converterId.length() == 0)) { - msg = - new StringBuffer("Empty conversion specifier starting at position "); - } else { - msg = new StringBuffer("Unrecognized conversion specifier ["); - msg.append(converterId); - msg.append("] starting at position "); - } - - msg.append(Integer.toString(i)); - msg.append(" in conversion pattern."); - - LogLog.error(msg.toString()); - - patternConverters.add( - new LiteralPatternConverter(currentLiteral.toString())); - formattingInfos.add(FormattingInfo.getDefault()); - } else { - patternConverters.add(pc); - formattingInfos.add(formattingInfo); - - if (currentLiteral.length() > 0) { - patternConverters.add( - new LiteralPatternConverter(currentLiteral.toString())); - formattingInfos.add(FormattingInfo.getDefault()); - } - } - - currentLiteral.setLength(0); - - return i; - } - - /** - * The class wraps another Map but throws exceptions on any attempt to modify the map. - */ - private static class ReadOnlyMap implements Map { - /** - * Wrapped map. - */ - private final Map map; - - /** - * Constructor - * @param src source map. - */ - public ReadOnlyMap(Map src) { - map = src; - } - - /** - * {@inheritDoc} - */ - public void clear() { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - */ - public boolean containsKey(Object key) { - return map.containsKey(key); - } - - /** - * {@inheritDoc} - */ - public boolean containsValue(Object value) { - return map.containsValue(value); - } - - /** - * {@inheritDoc} - */ - public Set entrySet() { - return map.entrySet(); - } - - /** - * {@inheritDoc} - */ - public Object get(Object key) { - return map.get(key); - } - - /** - * {@inheritDoc} - */ - public boolean isEmpty() { - return map.isEmpty(); - } - - /** - * {@inheritDoc} - */ - public Set keySet() { - return map.keySet(); - } - - /** - * {@inheritDoc} - */ - public Object put(Object key, Object value) { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - */ - public void putAll(Map t) { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - */ - public Object remove(Object key) { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - */ - public int size() { - return map.size(); - } - - /** - * {@inheritDoc} - */ - public Collection values() { - return map.values(); - } - } -} diff --git a/java/src/org/apache/log4j/pattern/PropertiesPatternConverter.java b/java/src/org/apache/log4j/pattern/PropertiesPatternConverter.java deleted file mode 100644 index a55cf97..0000000 --- a/java/src/org/apache/log4j/pattern/PropertiesPatternConverter.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; - -import java.util.Iterator; -import java.util.Set; -import org.apache.log4j.helpers.*; - - -/** - * Able to handle the contents of the LoggingEvent's Property bundle and either - * output the entire contents of the properties in a similar format to the - * java.util.Hashtable.toString(), or to output the value of a specific key - * within the property bundle - * when this pattern converter has the option set. - * - * @author Paul Smith - * @author Ceki Gülcü - */ -public final class PropertiesPatternConverter - extends LoggingEventPatternConverter { - /** - * Name of property to output. - */ - private final String option; - - /** - * Private constructor. - * @param options options, may be null. - */ - private PropertiesPatternConverter( - final String[] options) { - super( - ((options != null) && (options.length > 0)) - ? ("Property{" + options[0] + "}") : "Properties", "property"); - - if ((options != null) && (options.length > 0)) { - option = options[0]; - } else { - option = null; - } - } - - /** - * Obtains an instance of PropertiesPatternConverter. - * @param options options, may be null or first element contains name of property to format. - * @return instance of PropertiesPatternConverter. - */ - public static PropertiesPatternConverter newInstance( - final String[] options) { - return new PropertiesPatternConverter(options); - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - // if there is no additional options, we output every single - // Key/Value pair for the MDC in a similar format to Hashtable.toString() - if (option == null) { - toAppendTo.append("{"); - - try { - Set keySet = MDCKeySetExtractor.INSTANCE.getPropertyKeySet(event); - if (keySet != null) { - for (Iterator i = keySet.iterator(); i.hasNext();) { - Object item = i.next(); - Object val = event.getMDC(item.toString()); - toAppendTo.append("{").append(item).append(",").append(val).append( - "}"); - } - } - } catch(Exception ex) { - LogLog.error("Unexpected exception while extracting MDC keys", ex); - } - - toAppendTo.append("}"); - } else { - // otherwise they just want a single key output - Object val = event.getMDC(option); - - if (val != null) { - toAppendTo.append(val); - } - } - } -} diff --git a/java/src/org/apache/log4j/pattern/RelativeTimePatternConverter.java b/java/src/org/apache/log4j/pattern/RelativeTimePatternConverter.java deleted file mode 100644 index 007a29a..0000000 --- a/java/src/org/apache/log4j/pattern/RelativeTimePatternConverter.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Return the relative time in milliseconds since loading of the LoggingEvent - * class. - * - * @author Ceki Gülcü - */ -public class RelativeTimePatternConverter extends LoggingEventPatternConverter { - /** - * Cached formatted timestamp. - */ - private CachedTimestamp lastTimestamp = new CachedTimestamp(0, ""); - - /** - * Private constructor. - */ - public RelativeTimePatternConverter() { - super("Time", "time"); - } - - /** - * Obtains an instance of RelativeTimePatternConverter. - * @param options options, currently ignored, may be null. - * @return instance of RelativeTimePatternConverter. - */ - public static RelativeTimePatternConverter newInstance( - final String[] options) { - return new RelativeTimePatternConverter(); - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - long timestamp = event.timeStamp; - - if (!lastTimestamp.format(timestamp, toAppendTo)) { - final String formatted = - Long.toString(timestamp - LoggingEvent.getStartTime()); - toAppendTo.append(formatted); - lastTimestamp = new CachedTimestamp(timestamp, formatted); - } - } - - /** - * Cached timestamp and formatted value. - */ - private static final class CachedTimestamp { - /** - * Cached timestamp. - */ - private final long timestamp; - - /** - * Cached formatted timestamp. - */ - private final String formatted; - - /** - * Creates a new instance. - * @param timestamp timestamp. - * @param formatted formatted timestamp. - */ - public CachedTimestamp(long timestamp, final String formatted) { - this.timestamp = timestamp; - this.formatted = formatted; - } - - /** - * Appends the cached formatted timestamp to the buffer if timestamps match. - * @param newTimestamp requested timestamp. - * @param toAppendTo buffer to append formatted timestamp. - * @return true if requested timestamp matched cached timestamp. - */ - public boolean format(long newTimestamp, final StringBuffer toAppendTo) { - if (newTimestamp == timestamp) { - toAppendTo.append(formatted); - - return true; - } - - return false; - } - } -} diff --git a/java/src/org/apache/log4j/pattern/SequenceNumberPatternConverter.java b/java/src/org/apache/log4j/pattern/SequenceNumberPatternConverter.java deleted file mode 100644 index 8012682..0000000 --- a/java/src/org/apache/log4j/pattern/SequenceNumberPatternConverter.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Formats the event sequence number. - * - * @author Ceki Gülcü - */ -public class SequenceNumberPatternConverter - extends LoggingEventPatternConverter { - /** - * Singleton. - */ - private static final SequenceNumberPatternConverter INSTANCE = - new SequenceNumberPatternConverter(); - - /** - * Private constructor. - */ - private SequenceNumberPatternConverter() { - super("Sequence Number", "sn"); - } - - /** - * Obtains an instance of SequencePatternConverter. - * @param options options, currently ignored, may be null. - * @return instance of SequencePatternConverter. - */ - public static SequenceNumberPatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - toAppendTo.append("0"); - } -} diff --git a/java/src/org/apache/log4j/pattern/ThreadPatternConverter.java b/java/src/org/apache/log4j/pattern/ThreadPatternConverter.java deleted file mode 100644 index 6b3e6c3..0000000 --- a/java/src/org/apache/log4j/pattern/ThreadPatternConverter.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; - - -/** - * Formats the event thread name. - * - * @author Ceki Gülcü - */ -public class ThreadPatternConverter extends LoggingEventPatternConverter { - /** - * Singleton. - */ - private static final ThreadPatternConverter INSTANCE = - new ThreadPatternConverter(); - - /** - * Private constructor. - */ - private ThreadPatternConverter() { - super("Thread", "thread"); - } - - /** - * Obtains an instance of ThreadPatternConverter. - * @param options options, currently ignored, may be null. - * @return instance of ThreadPatternConverter. - */ - public static ThreadPatternConverter newInstance( - final String[] options) { - return INSTANCE; - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - toAppendTo.append(event.getThreadName()); - } -} diff --git a/java/src/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java b/java/src/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java deleted file mode 100644 index bf9c4b4..0000000 --- a/java/src/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.pattern; - -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.spi.ThrowableInformation; - - -/** - * Outputs the ThrowableInformation portion of the LoggingEvent. - * By default, outputs the full stack trace. %throwable{none} - * or %throwable{0} suppresses the stack trace. %throwable{short} - * or %throwable{1} outputs just the first line. %throwable{n} - * will output n lines for a positive integer or drop the last - * -n lines for a negative integer. - * - * @author Paul Smith - * - */ -public class ThrowableInformationPatternConverter - extends LoggingEventPatternConverter { - - /** - * Maximum lines of stack trace to output. - */ - private int maxLines = Integer.MAX_VALUE; - - /** - * Private constructor. - * @param options options, may be null. - */ - private ThrowableInformationPatternConverter( - final String[] options) { - super("Throwable", "throwable"); - - if ((options != null) && (options.length > 0)) { - if("none".equals(options[0])) { - maxLines = 0; - } else if("short".equals(options[0])) { - maxLines = 1; - } else { - try { - maxLines = Integer.parseInt(options[0]); - } catch(NumberFormatException ex) { - } - } - } - } - - /** - * Gets an instance of the class. - * @param options pattern options, may be null. If first element is "short", - * only the first line of the throwable will be formatted. - * @return instance of class. - */ - public static ThrowableInformationPatternConverter newInstance( - final String[] options) { - return new ThrowableInformationPatternConverter(options); - } - - /** - * {@inheritDoc} - */ - public void format(final LoggingEvent event, final StringBuffer toAppendTo) { - if (maxLines != 0) { - ThrowableInformation information = event.getThrowableInformation(); - - if (information != null) { - String[] stringRep = information.getThrowableStrRep(); - - int length = stringRep.length; - if (maxLines < 0) { - length += maxLines; - } else if (length > maxLines) { - length = maxLines; - } - - for (int i = 0; i < length; i++) { - String string = stringRep[i]; - toAppendTo.append(string).append("\n"); - } - } - } - } - - /** - * This converter obviously handles throwables. - * @return true. - */ - public boolean handlesThrowable() { - return true; - } -} diff --git a/java/src/org/apache/log4j/pattern/package.html b/java/src/org/apache/log4j/pattern/package.html deleted file mode 100644 index 1db8283..0000000 --- a/java/src/org/apache/log4j/pattern/package.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - -

Provides classes implementing format specifiers in conversion patterns.

- -
- diff --git a/java/src/org/apache/log4j/spi/AppenderAttachable.java b/java/src/org/apache/log4j/spi/AppenderAttachable.java deleted file mode 100644 index 89d7ef4..0000000 --- a/java/src/org/apache/log4j/spi/AppenderAttachable.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -import org.apache.log4j.Appender; -import java.util.Enumeration; - -/** - Interface for attaching appenders to objects. - - @author Ceki Gülcü - @since 0.9.1 */ -public interface AppenderAttachable { - - /** - Add an appender. - */ - public - void addAppender(Appender newAppender); - - /** - Get all previously added appenders as an Enumeration. */ - public - Enumeration getAllAppenders(); - - /** - Get an appender by name. - */ - public - Appender getAppender(String name); - - - /** - Returns true if the specified appender is in list of - attached attached, false otherwise. - - @since 1.2 */ - public - boolean isAttached(Appender appender); - - /** - Remove all previously added appenders. - */ - void removeAllAppenders(); - - - /** - Remove the appender passed as parameter from the list of appenders. - */ - void removeAppender(Appender appender); - - - /** - Remove the appender with the name passed as parameter from the - list of appenders. - */ - void - removeAppender(String name); -} - diff --git a/java/src/org/apache/log4j/spi/Configurator.java b/java/src/org/apache/log4j/spi/Configurator.java deleted file mode 100644 index 14fdd73..0000000 --- a/java/src/org/apache/log4j/spi/Configurator.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -import java.net.URL; - -/** - Implemented by classes capable of configuring log4j using a URL. - - @since 1.0 - @author Anders Kristensen - */ -public interface Configurator { - - /** - Special level value signifying inherited behaviour. The current - value of this string constant is inherited. {@link #NULL} - is a synonym. */ - public static final String INHERITED = "inherited"; - - /** - Special level signifying inherited behaviour, same as {@link - #INHERITED}. The current value of this string constant is - null. */ - public static final String NULL = "null"; - - - - /** - Interpret a resource pointed by a URL and set up log4j accordingly. - - The configuration is done relative to the hierarchy - parameter. - - @param url The URL to parse - @param repository The hierarchy to operation upon. - */ - void doConfigure(URL url, LoggerRepository repository); -} diff --git a/java/src/org/apache/log4j/spi/DefaultRepositorySelector.java b/java/src/org/apache/log4j/spi/DefaultRepositorySelector.java deleted file mode 100644 index 4b30752..0000000 --- a/java/src/org/apache/log4j/spi/DefaultRepositorySelector.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - - -package org.apache.log4j.spi; - - -public class DefaultRepositorySelector implements RepositorySelector { - - final LoggerRepository repository; - - public - DefaultRepositorySelector(LoggerRepository repository) { - this.repository = repository; - } - - public - LoggerRepository getLoggerRepository() { - return repository; - } -} - diff --git a/java/src/org/apache/log4j/spi/ErrorCode.java b/java/src/org/apache/log4j/spi/ErrorCode.java deleted file mode 100644 index b0e57f1..0000000 --- a/java/src/org/apache/log4j/spi/ErrorCode.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - - -/** - This interface defines commonly encoutered error codes. - - @author Ceki Gülcü - @since 0.9.0 - */ -public interface ErrorCode { - - public final int GENERIC_FAILURE = 0; - public final int WRITE_FAILURE = 1; - public final int FLUSH_FAILURE = 2; - public final int CLOSE_FAILURE = 3; - public final int FILE_OPEN_FAILURE = 4; - public final int MISSING_LAYOUT = 5; - public final int ADDRESS_PARSE_FAILURE = 6; -} diff --git a/java/src/org/apache/log4j/spi/ErrorHandler.java b/java/src/org/apache/log4j/spi/ErrorHandler.java deleted file mode 100644 index d629a2d..0000000 --- a/java/src/org/apache/log4j/spi/ErrorHandler.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -import org.apache.log4j.Appender; -import org.apache.log4j.Logger; - - -/** - Appenders may delegate their error handling to - ErrorHandlers. - -

Error handling is a particularly tedious to get right because by - definition errors are hard to predict and to reproduce. - - -

Please take the time to contact the author in case you discover - that errors are not properly handled. You are most welcome to - suggest new error handling policies or criticize existing policies. - - - @author Ceki Gülcü - -*/ -public interface ErrorHandler extends OptionHandler { - - /** - Add a reference to a logger to which the failing appender might - be attached to. The failing appender will be searched and - replaced only in the loggers you add through this method. - - @param logger One of the loggers that will be searched for the failing - appender in view of replacement. - - @since 1.2 */ - void setLogger(Logger logger); - - - /** - Equivalent to the {@link #error(String, Exception, int, - LoggingEvent event)} with the the event parameteter set to - null. - - */ - void error(String message, Exception e, int errorCode); - - /** - This method is normally used to just print the error message - passed as a parameter. - */ - void error(String message); - - /** - This method is invoked to handle the error. - - @param message The message assoicated with the error. - @param e The Exption that was thrown when the error occured. - @param errorCode The error code associated with the error. - @param event The logging event that the failing appender is asked - to log. - - @since 1.2 */ - void error(String message, Exception e, int errorCode, LoggingEvent event); - - /** - Set the appender for which errors are handled. This method is - usually called when the error handler is configured. - - @since 1.2 */ - void setAppender(Appender appender); - - /** - Set the appender to falkback upon in case of failure. - - @since 1.2 */ - void setBackupAppender(Appender appender); -} diff --git a/java/src/org/apache/log4j/spi/Filter.java b/java/src/org/apache/log4j/spi/Filter.java deleted file mode 100644 index 7bddbe8..0000000 --- a/java/src/org/apache/log4j/spi/Filter.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - - - -/** - Users should extend this class to implement customized logging - event filtering. Note that {@link org.apache.log4j.Category} and {@link - org.apache.log4j.AppenderSkeleton}, the parent class of all standard - appenders, have built-in filtering rules. It is suggested that you - first use and understand the built-in rules before rushing to write - your own custom filters. - -

This abstract class assumes and also imposes that filters be - organized in a linear chain. The {@link #decide - decide(LoggingEvent)} method of each filter is called sequentially, - in the order of their addition to the chain. - -

The {@link #decide decide(LoggingEvent)} method must return one - of the integer constants {@link #DENY}, {@link #NEUTRAL} or {@link - #ACCEPT}. - -

If the value {@link #DENY} is returned, then the log event is - dropped immediately without consulting with the remaining - filters. - -

If the value {@link #NEUTRAL} is returned, then the next filter - in the chain is consulted. If there are no more filters in the - chain, then the log event is logged. Thus, in the presence of no - filters, the default behaviour is to log all logging events. - -

If the value {@link #ACCEPT} is returned, then the log - event is logged without consulting the remaining filters. - -

The philosophy of log4j filters is largely inspired from the - Linux ipchains. - -

Note that filtering is only supported by the {@link - org.apache.log4j.xml.DOMConfigurator DOMConfigurator}. The {@link - org.apache.log4j.PropertyConfigurator PropertyConfigurator} does not - support filters. - - @author Ceki Gülcü - @since 0.9.0 */ -public abstract class Filter implements OptionHandler { - - /** - Points to the next filter in the filter chain. - - @deprecated As of 1.2.12, use {@link #getNext} and {@link #setNext} instead - */ - public Filter next; - - /** - The log event must be dropped immediately without consulting - with the remaining filters, if any, in the chain. */ - public static final int DENY = -1; - - /** - This filter is neutral with respect to the log event. The - remaining filters, if any, should be consulted for a final decision. - */ - public static final int NEUTRAL = 0; - - /** - The log event must be logged immediately without consulting with - the remaining filters, if any, in the chain. */ - public static final int ACCEPT = 1; - - - /** - Usually filters options become active when set. We provide a - default do-nothing implementation for convenience. - */ - public - void activateOptions() { - } - - - - /** -

If the decision is DENY, then the event will be - dropped. If the decision is NEUTRAL, then the next - filter, if any, will be invoked. If the decision is ACCEPT then - the event will be logged without consulting with other filters in - the chain. - - @param event The LoggingEvent to decide upon. - @return decision The decision of the filter. */ - abstract - public - int decide(LoggingEvent event); - - /** - * Set the next filter pointer. - */ - public void setNext(Filter next) { - this.next = next; - } - - /** - * Return the pointer to the next filter; - */ - public Filter getNext() { - return next; - } - -} diff --git a/java/src/org/apache/log4j/spi/HierarchyEventListener.java b/java/src/org/apache/log4j/spi/HierarchyEventListener.java deleted file mode 100644 index 77a0efd..0000000 --- a/java/src/org/apache/log4j/spi/HierarchyEventListener.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -import org.apache.log4j.Appender; -import org.apache.log4j.Category; - -/** - Listen to events occuring within a {@link - org.apache.log4j.Hierarchy Hierarchy}. - - @author Ceki Gülcü - @since 1.2 - - */ -public interface HierarchyEventListener { - - - //public - //void categoryCreationEvent(Category cat); - - - public - void addAppenderEvent(Category cat, Appender appender); - - public - void removeAppenderEvent(Category cat, Appender appender); - - -} diff --git a/java/src/org/apache/log4j/spi/LocationInfo.java b/java/src/org/apache/log4j/spi/LocationInfo.java deleted file mode 100644 index 55ee5c3..0000000 --- a/java/src/org/apache/log4j/spi/LocationInfo.java +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contributors: Mathias Rupprecht - -package org.apache.log4j.spi; - -import org.apache.log4j.Layout; -import org.apache.log4j.helpers.LogLog; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.InterruptedIOException; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; - -/** - The internal representation of caller location information. - - @since 0.8.3 -*/ -public class LocationInfo implements java.io.Serializable { - - /** - Caller's line number. - */ - transient String lineNumber; - /** - Caller's file name. - */ - transient String fileName; - /** - Caller's fully qualified class name. - */ - transient String className; - /** - Caller's method name. - */ - transient String methodName; - /** - All available caller information, in the format - fully.qualified.classname.of.caller.methodName(Filename.java:line) - */ - public String fullInfo; - - private static StringWriter sw = new StringWriter(); - private static PrintWriter pw = new PrintWriter(sw); - - private static Method getStackTraceMethod; - private static Method getClassNameMethod; - private static Method getMethodNameMethod; - private static Method getFileNameMethod; - private static Method getLineNumberMethod; - - - /** - When location information is not available the constant - NA is returned. Current value of this string - constant is ?. */ - public final static String NA = "?"; - - static final long serialVersionUID = -1325822038990805636L; - - /** - * NA_LOCATION_INFO is provided for compatibility with log4j 1.3. - * @since 1.2.15 - */ - public static final LocationInfo NA_LOCATION_INFO = - new LocationInfo(NA, NA, NA, NA); - - - - // Check if we are running in IBM's visual age. - static boolean inVisualAge = false; - static { - try { - inVisualAge = Class.forName("com.ibm.uvm.tools.DebugSupport") != null; - LogLog.debug("Detected IBM VisualAge environment."); - } catch(Throwable e) { - // nothing to do - } - try { - Class[] noArgs = null; - getStackTraceMethod = Throwable.class.getMethod("getStackTrace", noArgs); - Class stackTraceElementClass = Class.forName("java.lang.StackTraceElement"); - getClassNameMethod = stackTraceElementClass.getMethod("getClassName", noArgs); - getMethodNameMethod = stackTraceElementClass.getMethod("getMethodName", noArgs); - getFileNameMethod = stackTraceElementClass.getMethod("getFileName", noArgs); - getLineNumberMethod = stackTraceElementClass.getMethod("getLineNumber", noArgs); - } catch(ClassNotFoundException ex) { - LogLog.debug("LocationInfo will use pre-JDK 1.4 methods to determine location."); - } catch(NoSuchMethodException ex) { - LogLog.debug("LocationInfo will use pre-JDK 1.4 methods to determine location."); - } - } - - /** - Instantiate location information based on a Throwable. We - expect the Throwable t, to be in the format - -

-        java.lang.Throwable
-        ...
-          at org.apache.log4j.PatternLayout.format(PatternLayout.java:413)
-          at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183)
-        at org.apache.log4j.Category.callAppenders(Category.java:131)
-        at org.apache.log4j.Category.log(Category.java:512)
-        at callers.fully.qualified.className.methodName(FileName.java:74)
-	...
-       
- -

However, we can also deal with JIT compilers that "lose" the - location information, especially between the parentheses. - @param t throwable used to determine location, may be null. - @param fqnOfCallingClass class name of first class considered part of - the logging framework. Location will be site that calls a method on this class. - - */ - public LocationInfo(Throwable t, String fqnOfCallingClass) { - if(t == null || fqnOfCallingClass == null) - return; - if (getLineNumberMethod != null) { - try { - Object[] noArgs = null; - Object[] elements = (Object[]) getStackTraceMethod.invoke(t, noArgs); - String prevClass = NA; - for(int i = elements.length - 1; i >= 0; i--) { - String thisClass = (String) getClassNameMethod.invoke(elements[i], noArgs); - if(fqnOfCallingClass.equals(thisClass)) { - int caller = i + 1; - if (caller < elements.length) { - className = prevClass; - methodName = (String) getMethodNameMethod.invoke(elements[caller], noArgs); - fileName = (String) getFileNameMethod.invoke(elements[caller], noArgs); - if (fileName == null) { - fileName = NA; - } - int line = ((Integer) getLineNumberMethod.invoke(elements[caller], noArgs)).intValue(); - if (line < 0) { - lineNumber = NA; - } else { - lineNumber = String.valueOf(line); - } - StringBuffer buf = new StringBuffer(); - buf.append(className); - buf.append("."); - buf.append(methodName); - buf.append("("); - buf.append(fileName); - buf.append(":"); - buf.append(lineNumber); - buf.append(")"); - this.fullInfo = buf.toString(); - } - return; - } - prevClass = thisClass; - } - return; - } catch(IllegalAccessException ex) { - LogLog.debug("LocationInfo failed using JDK 1.4 methods", ex); - } catch(InvocationTargetException ex) { - if (ex.getTargetException() instanceof InterruptedException - || ex.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.debug("LocationInfo failed using JDK 1.4 methods", ex); - } catch(RuntimeException ex) { - LogLog.debug("LocationInfo failed using JDK 1.4 methods", ex); - } - } - - String s; - // Protect against multiple access to sw. - synchronized(sw) { - t.printStackTrace(pw); - s = sw.toString(); - sw.getBuffer().setLength(0); - } - //System.out.println("s is ["+s+"]."); - int ibegin, iend; - - // Given the current structure of the package, the line - // containing "org.apache.log4j.Category." should be printed just - // before the caller. - - // This method of searching may not be fastest but it's safer - // than counting the stack depth which is not guaranteed to be - // constant across JVM implementations. - ibegin = s.lastIndexOf(fqnOfCallingClass); - if(ibegin == -1) - return; - - // - // if the next character after the class name exists - // but is not a period, see if the classname is - // followed by a period earlier in the trace. - // Minimizes mistakeningly matching on a class whose - // name is a substring of the desired class. - // See bug 44888. - if (ibegin + fqnOfCallingClass.length() < s.length() && - s.charAt(ibegin + fqnOfCallingClass.length()) != '.') { - int i = s.lastIndexOf(fqnOfCallingClass + "."); - if (i != -1) { - ibegin = i; - } - } - - - ibegin = s.indexOf(Layout.LINE_SEP, ibegin); - if(ibegin == -1) - return; - ibegin+= Layout.LINE_SEP_LEN; - - // determine end of line - iend = s.indexOf(Layout.LINE_SEP, ibegin); - if(iend == -1) - return; - - // VA has a different stack trace format which doesn't - // need to skip the inital 'at' - if(!inVisualAge) { - // back up to first blank character - ibegin = s.lastIndexOf("at ", iend); - if(ibegin == -1) - return; - // Add 3 to skip "at "; - ibegin += 3; - } - // everything between is the requested stack item - this.fullInfo = s.substring(ibegin, iend); - } - - /** - * Appends a location fragment to a buffer to build the - * full location info. - * @param buf StringBuffer to receive content. - * @param fragment fragment of location (class, method, file, line), - * if null the value of NA will be appended. - * @since 1.2.15 - */ - private static final void appendFragment(final StringBuffer buf, - final String fragment) { - if (fragment == null) { - buf.append(NA); - } else { - buf.append(fragment); - } - } - - /** - * Create new instance. - * @param file source file name - * @param classname class name - * @param method method - * @param line source line number - * - * @since 1.2.15 - */ - public LocationInfo( - final String file, - final String classname, - final String method, - final String line) { - this.fileName = file; - this.className = classname; - this.methodName = method; - this.lineNumber = line; - StringBuffer buf = new StringBuffer(); - appendFragment(buf, classname); - buf.append("."); - appendFragment(buf, method); - buf.append("("); - appendFragment(buf, file); - buf.append(":"); - appendFragment(buf, line); - buf.append(")"); - this.fullInfo = buf.toString(); - } - - /** - Return the fully qualified class name of the caller making the - logging request. - */ - public - String getClassName() { - if(fullInfo == null) return NA; - if(className == null) { - // Starting the search from '(' is safer because there is - // potentially a dot between the parentheses. - int iend = fullInfo.lastIndexOf('('); - if(iend == -1) - className = NA; - else { - iend =fullInfo.lastIndexOf('.', iend); - - // This is because a stack trace in VisualAge looks like: - - //java.lang.RuntimeException - // java.lang.Throwable() - // java.lang.Exception() - // java.lang.RuntimeException() - // void test.test.B.print() - // void test.test.A.printIndirect() - // void test.test.Run.main(java.lang.String []) - int ibegin = 0; - if (inVisualAge) { - ibegin = fullInfo.lastIndexOf(' ', iend)+1; - } - - if(iend == -1) - className = NA; - else - className = this.fullInfo.substring(ibegin, iend); - } - } - return className; - } - - /** - Return the file name of the caller. - -

This information is not always available. - */ - public - String getFileName() { - if(fullInfo == null) return NA; - - if(fileName == null) { - int iend = fullInfo.lastIndexOf(':'); - if(iend == -1) - fileName = NA; - else { - int ibegin = fullInfo.lastIndexOf('(', iend - 1); - fileName = this.fullInfo.substring(ibegin + 1, iend); - } - } - return fileName; - } - - /** - Returns the line number of the caller. - -

This information is not always available. - */ - public - String getLineNumber() { - if(fullInfo == null) return NA; - - if(lineNumber == null) { - int iend = fullInfo.lastIndexOf(')'); - int ibegin = fullInfo.lastIndexOf(':', iend -1); - if(ibegin == -1) - lineNumber = NA; - else - lineNumber = this.fullInfo.substring(ibegin + 1, iend); - } - return lineNumber; - } - - /** - Returns the method name of the caller. - */ - public - String getMethodName() { - if(fullInfo == null) return NA; - if(methodName == null) { - int iend = fullInfo.lastIndexOf('('); - int ibegin = fullInfo.lastIndexOf('.', iend); - if(ibegin == -1) - methodName = NA; - else - methodName = this.fullInfo.substring(ibegin + 1, iend); - } - return methodName; - } -} diff --git a/java/src/org/apache/log4j/spi/LoggerFactory.java b/java/src/org/apache/log4j/spi/LoggerFactory.java deleted file mode 100644 index 568c41f..0000000 --- a/java/src/org/apache/log4j/spi/LoggerFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -import org.apache.log4j.Logger; - -/** - - Implement this interface to create new instances of Logger or - a sub-class of Logger. - -

See examples/subclass/MyLogger.java for an example. - - @author Ceki Gülcü - @since version 0.8.5 - - */ -public interface LoggerFactory { - - public - Logger makeNewLoggerInstance(String name); - -} diff --git a/java/src/org/apache/log4j/spi/LoggerRepository.java b/java/src/org/apache/log4j/spi/LoggerRepository.java deleted file mode 100644 index 9ca156b..0000000 --- a/java/src/org/apache/log4j/spi/LoggerRepository.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -import java.util.Enumeration; - -import org.apache.log4j.Appender; -import org.apache.log4j.Category; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; - -/** - A LoggerRepository is used to create and retrieve - Loggers. The relation between loggers in a repository - depends on the repository but typically loggers are arranged in a - named hierarchy. - -

In addition to the creational methods, a - LoggerRepository can be queried for existing loggers, - can act as a point of registry for events related to loggers. - - @author Ceki Gülcü - @since 1.2 */ -public interface LoggerRepository { - - /** - Add a {@link HierarchyEventListener} event to the repository. - */ - public - void addHierarchyEventListener(HierarchyEventListener listener); - - /** - Returns whether this repository is disabled for a given - level. The answer depends on the repository threshold and the - level parameter. See also {@link #setThreshold} - method. */ - boolean isDisabled(int level); - - /** - Set the repository-wide threshold. All logging requests below the - threshold are immediately dropped. By default, the threshold is - set to Level.ALL which has the lowest possible rank. */ - public - void setThreshold(Level level); - - /** - Another form of {@link #setThreshold(Level)} accepting a string - parameter instead of a Level. */ - public - void setThreshold(String val); - - public - void emitNoAppenderWarning(Category cat); - - /** - Get the repository-wide threshold. See {@link - #setThreshold(Level)} for an explanation. */ - public - Level getThreshold(); - - public - Logger getLogger(String name); - - public - Logger getLogger(String name, LoggerFactory factory); - - public - Logger getRootLogger(); - - public - abstract - Logger exists(String name); - - public - abstract - void shutdown(); - - public - Enumeration getCurrentLoggers(); - - /** - Deprecated. Please use {@link #getCurrentLoggers} instead. */ - public - Enumeration getCurrentCategories(); - - - public - abstract - void fireAddAppenderEvent(Category logger, Appender appender); - - public - abstract - void resetConfiguration(); - -} diff --git a/java/src/org/apache/log4j/spi/LoggingEvent.java b/java/src/org/apache/log4j/spi/LoggingEvent.java deleted file mode 100644 index 2bacb50..0000000 --- a/java/src/org/apache/log4j/spi/LoggingEvent.java +++ /dev/null @@ -1,639 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -import java.io.InterruptedIOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Collections; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; -import java.util.Set; - -import org.apache.log4j.Category; -import org.apache.log4j.Level; -import org.apache.log4j.MDC; -import org.apache.log4j.NDC; -import org.apache.log4j.Priority; -import org.apache.log4j.helpers.Loader; -import org.apache.log4j.helpers.LogLog; - -// Contributors: Nelson Minar -// Wolf Siberski -// Anders Kristensen - -/** - The internal representation of logging events. When an affirmative - decision is made to log then a LoggingEvent instance - is created. This instance is passed around to the different log4j - components. - -

This class is of concern to those wishing to extend log4j. - - @author Ceki Gülcü - @author James P. Cakalic - - @since 0.8.2 */ -public class LoggingEvent implements java.io.Serializable { - - private static long startTime = System.currentTimeMillis(); - - /** Fully qualified name of the calling category class. */ - transient public final String fqnOfCategoryClass; - - /** - * The category of the logging event. This field is not serialized - * for performance reasons. - * - *

It is set by the LoggingEvent constructor or set by a remote - * entity after deserialization. - * - * @deprecated This field will be marked as private or be completely - * removed in future releases. Please do not use it. - * */ - transient private Category logger; - - /** - *

The category (logger) name. - * - * @deprecated This field will be marked as private in future - * releases. Please do not access it directly. Use the {@link - * #getLoggerName} method instead. - - * */ - final public String categoryName; - - /** - * Level of logging event. Level cannot be serializable because it - * is a flyweight. Due to its special seralization it cannot be - * declared final either. - * - *

This field should not be accessed directly. You shoud use the - * {@link #getLevel} method instead. - * - * @deprecated This field will be marked as private in future - * releases. Please do not access it directly. Use the {@link - * #getLevel} method instead. - * */ - transient public Priority level; - - /** The nested diagnostic context (NDC) of logging event. */ - private String ndc; - - /** The mapped diagnostic context (MDC) of logging event. */ - private Hashtable mdcCopy; - - - /** Have we tried to do an NDC lookup? If we did, there is no need - * to do it again. Note that its value is always false when - * serialized. Thus, a receiving SocketNode will never use it's own - * (incorrect) NDC. See also writeObject method. */ - private boolean ndcLookupRequired = true; - - - /** Have we tried to do an MDC lookup? If we did, there is no need - * to do it again. Note that its value is always false when - * serialized. See also the getMDC and getMDCCopy methods. */ - private boolean mdcCopyLookupRequired = true; - - /** The application supplied message of logging event. */ - transient private Object message; - - /** The application supplied message rendered through the log4j - objet rendering mechanism.*/ - private String renderedMessage; - - /** The name of thread in which this logging event was generated. */ - private String threadName; - - - /** This - variable contains information about this event's throwable - */ - private ThrowableInformation throwableInfo; - - /** The number of milliseconds elapsed from 1/1/1970 until logging event - was created. */ - public final long timeStamp; - /** Location information for the caller. */ - private LocationInfo locationInfo; - - // Serialization - static final long serialVersionUID = -868428216207166145L; - - static final Integer[] PARAM_ARRAY = new Integer[1]; - static final String TO_LEVEL = "toLevel"; - static final Class[] TO_LEVEL_PARAMS = new Class[] {int.class}; - static final Hashtable methodCache = new Hashtable(3); // use a tiny table - - /** - Instantiate a LoggingEvent from the supplied parameters. - -

Except {@link #timeStamp} all the other fields of - LoggingEvent are filled when actually needed. -

- @param logger The logger generating this event. - @param level The level of this event. - @param message The message of this event. - @param throwable The throwable of this event. */ - public LoggingEvent(String fqnOfCategoryClass, Category logger, - Priority level, Object message, Throwable throwable) { - this.fqnOfCategoryClass = fqnOfCategoryClass; - this.logger = logger; - this.categoryName = logger.getName(); - this.level = level; - this.message = message; - if(throwable != null) { - this.throwableInfo = new ThrowableInformation(throwable, logger); - } - timeStamp = System.currentTimeMillis(); - } - - /** - Instantiate a LoggingEvent from the supplied parameters. - -

Except {@link #timeStamp} all the other fields of - LoggingEvent are filled when actually needed. -

- @param logger The logger generating this event. - @param timeStamp the timestamp of this logging event - @param level The level of this event. - @param message The message of this event. - @param throwable The throwable of this event. */ - public LoggingEvent(String fqnOfCategoryClass, Category logger, - long timeStamp, Priority level, Object message, - Throwable throwable) { - this.fqnOfCategoryClass = fqnOfCategoryClass; - this.logger = logger; - this.categoryName = logger.getName(); - this.level = level; - this.message = message; - if(throwable != null) { - this.throwableInfo = new ThrowableInformation(throwable, logger); - } - - this.timeStamp = timeStamp; - } - - /** - Create new instance. - @since 1.2.15 - @param fqnOfCategoryClass Fully qualified class name - of Logger implementation. - @param logger The logger generating this event. - @param timeStamp the timestamp of this logging event - @param level The level of this event. - @param message The message of this event. - @param threadName thread name - @param throwable The throwable of this event. - @param ndc Nested diagnostic context - @param info Location info - @param properties MDC properties - */ - public LoggingEvent(final String fqnOfCategoryClass, - final Category logger, - final long timeStamp, - final Level level, - final Object message, - final String threadName, - final ThrowableInformation throwable, - final String ndc, - final LocationInfo info, - final java.util.Map properties) { - super(); - this.fqnOfCategoryClass = fqnOfCategoryClass; - this.logger = logger; - if (logger != null) { - categoryName = logger.getName(); - } else { - categoryName = null; - } - this.level = level; - this.message = message; - if(throwable != null) { - this.throwableInfo = throwable; - } - - this.timeStamp = timeStamp; - this.threadName = threadName; - ndcLookupRequired = false; - this.ndc = ndc; - this.locationInfo = info; - mdcCopyLookupRequired = false; - if (properties != null) { - mdcCopy = new java.util.Hashtable(properties); - } - } - - - /** - Set the location information for this logging event. The collected - information is cached for future use. - */ - public LocationInfo getLocationInformation() { - if(locationInfo == null) { - locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass); - } - return locationInfo; - } - - /** - * Return the level of this event. Use this form instead of directly - * accessing the level field. */ - public Level getLevel() { - return (Level) level; - } - - /** - * Return the name of the logger. Use this form instead of directly - * accessing the categoryName field. - */ - public String getLoggerName() { - return categoryName; - } - - /** - * Gets the logger of the event. - * Use should be restricted to cloning events. - * @since 1.2.15 - */ - public Category getLogger() { - return logger; - } - - /** - Return the message for this logging event. - -

Before serialization, the returned object is the message - passed by the user to generate the logging event. After - serialization, the returned value equals the String form of the - message possibly after object rendering. - - @since 1.1 */ - public - Object getMessage() { - if(message != null) { - return message; - } else { - return getRenderedMessage(); - } - } - - /** - * This method returns the NDC for this event. It will return the - * correct content even if the event was generated in a different - * thread or even on a different machine. The {@link NDC#get} method - * should never be called directly. */ - public - String getNDC() { - if(ndcLookupRequired) { - ndcLookupRequired = false; - ndc = NDC.get(); - } - return ndc; - } - - - /** - Returns the the context corresponding to the key - parameter. If there is a local MDC copy, possibly because we are - in a logging server or running inside AsyncAppender, then we - search for the key in MDC copy, if a value is found it is - returned. Otherwise, if the search in MDC copy returns a null - result, then the current thread's MDC is used. - -

Note that both the local MDC copy and the current - thread's MDC are searched. - - */ - public - Object getMDC(String key) { - Object r; - // Note the mdcCopy is used if it exists. Otherwise we use the MDC - // that is associated with the thread. - if(mdcCopy != null) { - r = mdcCopy.get(key); - if(r != null) { - return r; - } - } - return MDC.get(key); - } - - /** - Obtain a copy of this thread's MDC prior to serialization or - asynchronous logging. - */ - public - void getMDCCopy() { - if(mdcCopyLookupRequired) { - mdcCopyLookupRequired = false; - // the clone call is required for asynchronous logging. - // See also bug #5932. - Hashtable t = (Hashtable) MDC.getContext(); - if(t != null) { - mdcCopy = (Hashtable) t.clone(); - } - } - } - - public - String getRenderedMessage() { - if(renderedMessage == null && message != null) { - if(message instanceof String) - renderedMessage = (String) message; - else { - LoggerRepository repository = logger.getLoggerRepository(); - - if(repository instanceof RendererSupport) { - RendererSupport rs = (RendererSupport) repository; - renderedMessage= rs.getRendererMap().findAndRender(message); - } else { - renderedMessage = message.toString(); - } - } - } - return renderedMessage; - } - - /** - Returns the time when the application started, in milliseconds - elapsed since 01.01.1970. */ - public static long getStartTime() { - return startTime; - } - - public - String getThreadName() { - if(threadName == null) - threadName = (Thread.currentThread()).getName(); - return threadName; - } - - /** - Returns the throwable information contained within this - event. May be null if there is no such information. - -

Note that the {@link Throwable} object contained within a - {@link ThrowableInformation} does not survive serialization. - - @since 1.1 */ - public - ThrowableInformation getThrowableInformation() { - return throwableInfo; - } - - /** - Return this event's throwable's string[] representaion. - */ - public - String[] getThrowableStrRep() { - - if(throwableInfo == null) - return null; - else - return throwableInfo.getThrowableStrRep(); - } - - - private - void readLevel(ObjectInputStream ois) - throws java.io.IOException, ClassNotFoundException { - - int p = ois.readInt(); - try { - String className = (String) ois.readObject(); - if(className == null) { - level = Level.toLevel(p); - } else { - Method m = (Method) methodCache.get(className); - if(m == null) { - Class clazz = Loader.loadClass(className); - // Note that we use Class.getDeclaredMethod instead of - // Class.getMethod. This assumes that the Level subclass - // implements the toLevel(int) method which is a - // requirement. Actually, it does not make sense for Level - // subclasses NOT to implement this method. Also note that - // only Level can be subclassed and not Priority. - m = clazz.getDeclaredMethod(TO_LEVEL, TO_LEVEL_PARAMS); - methodCache.put(className, m); - } - PARAM_ARRAY[0] = new Integer(p); - level = (Level) m.invoke(null, PARAM_ARRAY); - } - } catch(InvocationTargetException e) { - if (e.getTargetException() instanceof InterruptedException - || e.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.warn("Level deserialization failed, reverting to default.", e); - level = Level.toLevel(p); - } catch(NoSuchMethodException e) { - LogLog.warn("Level deserialization failed, reverting to default.", e); - level = Level.toLevel(p); - } catch(IllegalAccessException e) { - LogLog.warn("Level deserialization failed, reverting to default.", e); - level = Level.toLevel(p); - } catch(RuntimeException e) { - LogLog.warn("Level deserialization failed, reverting to default.", e); - level = Level.toLevel(p); - } - } - - private void readObject(ObjectInputStream ois) - throws java.io.IOException, ClassNotFoundException { - ois.defaultReadObject(); - readLevel(ois); - - // Make sure that no location info is available to Layouts - if(locationInfo == null) - locationInfo = new LocationInfo(null, null); - } - - private - void writeObject(ObjectOutputStream oos) throws java.io.IOException { - // Aside from returning the current thread name the wgetThreadName - // method sets the threadName variable. - this.getThreadName(); - - // This sets the renders the message in case it wasn't up to now. - this.getRenderedMessage(); - - // This call has a side effect of setting this.ndc and - // setting ndcLookupRequired to false if not already false. - this.getNDC(); - - // This call has a side effect of setting this.mdcCopy and - // setting mdcLookupRequired to false if not already false. - this.getMDCCopy(); - - // This sets the throwable sting representation of the event throwable. - this.getThrowableStrRep(); - - oos.defaultWriteObject(); - - // serialize this event's level - writeLevel(oos); - } - - private - void writeLevel(ObjectOutputStream oos) throws java.io.IOException { - - oos.writeInt(level.toInt()); - - Class clazz = level.getClass(); - if(clazz == Level.class) { - oos.writeObject(null); - } else { - // writing directly the Class object would be nicer, except that - // serialized a Class object can not be read back by JDK - // 1.1.x. We have to resort to this hack instead. - oos.writeObject(clazz.getName()); - } - } - - /** - * Set value for MDC property. - * This adds the specified MDC property to the event. - * Access to the MDC is not synchronized, so this - * method should only be called when it is known that - * no other threads are accessing the MDC. - * @since 1.2.15 - * @param propName - * @param propValue - */ - public final void setProperty(final String propName, - final String propValue) { - if (mdcCopy == null) { - getMDCCopy(); - } - if (mdcCopy == null) { - mdcCopy = new Hashtable(); - } - mdcCopy.put(propName, propValue); - } - - /** - * Return a property for this event. The return value can be null. - * - * Equivalent to getMDC(String) in log4j 1.2. Provided - * for compatibility with log4j 1.3. - * - * @param key property name - * @return property value or null if property not set - * @since 1.2.15 - */ - public final String getProperty(final String key) { - Object value = getMDC(key); - String retval = null; - if (value != null) { - retval = value.toString(); - } - return retval; - } - - /** - * Check for the existence of location information without creating it - * (a byproduct of calling getLocationInformation). - * @return true if location information has been extracted. - * @since 1.2.15 - */ - public final boolean locationInformationExists() { - return (locationInfo != null); - } - - /** - * Getter for the event's time stamp. The time stamp is calculated starting - * from 1970-01-01 GMT. - * @return timestamp - * - * @since 1.2.15 - */ - public final long getTimeStamp() { - return timeStamp; - } - - /** - * Returns the set of the key values in the properties - * for the event. - * - * The returned set is unmodifiable by the caller. - * - * Provided for compatibility with log4j 1.3 - * - * @return Set an unmodifiable set of the property keys. - * @since 1.2.15 - */ - public Set getPropertyKeySet() { - return getProperties().keySet(); - } - - /** - * Returns the set of properties - * for the event. - * - * The returned set is unmodifiable by the caller. - * - * Provided for compatibility with log4j 1.3 - * - * @return Set an unmodifiable map of the properties. - * @since 1.2.15 - */ - public Map getProperties() { - getMDCCopy(); - Map properties; - if (mdcCopy == null) { - properties = new HashMap(); - } else { - properties = mdcCopy; - } - return Collections.unmodifiableMap(properties); - } - - /** - * Get the fully qualified name of the calling logger sub-class/wrapper. - * Provided for compatibility with log4j 1.3 - * @return fully qualified class name, may be null. - * @since 1.2.15 - */ - public String getFQNOfLoggerClass() { - return fqnOfCategoryClass; - } - - - /** - * This removes the specified MDC property from the event. - * Access to the MDC is not synchronized, so this - * method should only be called when it is known that - * no other threads are accessing the MDC. - * @param propName the property name to remove - * @since 1.2.16 - */ - public Object removeProperty(String propName) { - if (mdcCopy == null) { - getMDCCopy(); - } - if (mdcCopy == null) { - mdcCopy = new Hashtable(); - } - return mdcCopy.remove(propName); - } -} diff --git a/java/src/org/apache/log4j/spi/NOPLogger.java b/java/src/org/apache/log4j/spi/NOPLogger.java deleted file mode 100644 index 38b1514..0000000 --- a/java/src/org/apache/log4j/spi/NOPLogger.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.spi; - -import org.apache.log4j.Appender; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.Priority; - -import java.util.Enumeration; -import java.util.ResourceBundle; -import java.util.Vector; - -/** - * No-operation implementation of Logger used by NOPLoggerRepository. - * @since 1.2.15 - */ -public final class NOPLogger extends Logger { - /** - * Create instance of Logger. - * @param repo repository, may not be null. - * @param name name, may not be null, use "root" for root logger. - */ - public NOPLogger(NOPLoggerRepository repo, final String name) { - super(name); - this.repository = repo; - this.level = Level.OFF; - this.parent = this; - } - - /** {@inheritDoc} */ - public void addAppender(final Appender newAppender) { - } - - /** {@inheritDoc} */ - public void assertLog(final boolean assertion, final String msg) { - } - - - /** {@inheritDoc} */ - public void callAppenders(final LoggingEvent event) { - } - - /** {@inheritDoc} */ - void closeNestedAppenders() { - } - - /** {@inheritDoc} */ - public void debug(final Object message) { - } - - - /** {@inheritDoc} */ - public void debug(final Object message, final Throwable t) { - } - - /** {@inheritDoc} */ - public void error(final Object message) { - } - - /** {@inheritDoc} */ - public void error(final Object message, final Throwable t) { - } - - - /** {@inheritDoc} */ - public void fatal(final Object message) { - } - - /** {@inheritDoc} */ - public void fatal(final Object message, final Throwable t) { - } - - - /** {@inheritDoc} */ - public Enumeration getAllAppenders() { - return new Vector().elements(); - } - - /** {@inheritDoc} */ - public Appender getAppender(final String name) { - return null; - } - - /** {@inheritDoc} */ - public Level getEffectiveLevel() { - return Level.OFF; - } - - /** {@inheritDoc} */ - public Priority getChainedPriority() { - return getEffectiveLevel(); - } - - /** {@inheritDoc} */ - public ResourceBundle getResourceBundle() { - return null; - } - - - /** {@inheritDoc} */ - public void info(final Object message) { - } - - /** {@inheritDoc} */ - public void info(final Object message, final Throwable t) { - } - - /** {@inheritDoc} */ - public boolean isAttached(Appender appender) { - return false; - } - - /** {@inheritDoc} */ - public boolean isDebugEnabled() { - return false; - } - - /** {@inheritDoc} */ - public boolean isEnabledFor(final Priority level) { - return false; - } - - /** {@inheritDoc} */ - public boolean isInfoEnabled() { - return false; - } - - - /** {@inheritDoc} */ - public void l7dlog(final Priority priority, final String key, final Throwable t) { - } - - /** {@inheritDoc} */ - public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) { - } - - /** {@inheritDoc} */ - public void log(final Priority priority, final Object message, final Throwable t) { - } - - /** {@inheritDoc} */ - public void log(final Priority priority, final Object message) { - } - - /** {@inheritDoc} */ - public void log(final String callerFQCN, final Priority level, final Object message, final Throwable t) { - } - - /** {@inheritDoc} */ - public void removeAllAppenders() { - } - - - /** {@inheritDoc} */ - public void removeAppender(Appender appender) { - } - - /** {@inheritDoc} */ - public void removeAppender(final String name) { - } - - /** {@inheritDoc} */ - public void setLevel(final Level level) { - } - - - /** {@inheritDoc} */ - public void setPriority(final Priority priority) { - } - - /** {@inheritDoc} */ - public void setResourceBundle(final ResourceBundle bundle) { - } - - /** {@inheritDoc} */ - public void warn(final Object message) { - } - - /** {@inheritDoc} */ - public void warn(final Object message, final Throwable t) { - } - - /** {@inheritDoc} */ - public void trace(Object message) { - } - - /** {@inheritDoc} */ - public void trace(Object message, Throwable t) { - } - - /** {@inheritDoc} */ - public boolean isTraceEnabled() { - return false; - } - - -} diff --git a/java/src/org/apache/log4j/spi/NOPLoggerRepository.java b/java/src/org/apache/log4j/spi/NOPLoggerRepository.java deleted file mode 100644 index bdb6ed9..0000000 --- a/java/src/org/apache/log4j/spi/NOPLoggerRepository.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.spi; - -import org.apache.log4j.Level; -import org.apache.log4j.Category; -import org.apache.log4j.Logger; -import org.apache.log4j.Appender; - -import java.util.Enumeration; -import java.util.Vector; - -/** - * No-operation implementation of LoggerRepository which is used when - * LogManager.repositorySelector is erroneously nulled during class reloading. - * @since 1.2.15 - */ -public final class NOPLoggerRepository implements LoggerRepository { - /** - * {@inheritDoc} - */ - public void addHierarchyEventListener(final HierarchyEventListener listener) { - } - - /** - * {@inheritDoc} - */ - public boolean isDisabled(final int level) { - return true; - } - - /** - * {@inheritDoc} - */ - public void setThreshold(final Level level) { - } - - /** - * {@inheritDoc} - */ - public void setThreshold(final String val) { - } - - /** - * {@inheritDoc} - */ - public void emitNoAppenderWarning(final Category cat) { - } - - /** - * {@inheritDoc} - */ - public Level getThreshold() { - return Level.OFF; - } - - /** - * {@inheritDoc} - */ - public Logger getLogger(final String name) { - return new NOPLogger(this, name); - } - - /** - * {@inheritDoc} - */ - public Logger getLogger(final String name, final LoggerFactory factory) { - return new NOPLogger(this, name); - } - - /** - * {@inheritDoc} - */ - public Logger getRootLogger() { - return new NOPLogger(this, "root"); - } - - /** - * {@inheritDoc} - */ - public Logger exists(final String name) { - return null; - } - - /** - * {@inheritDoc} - */ - public void shutdown() { - } - - /** - * {@inheritDoc} - */ - public Enumeration getCurrentLoggers() { - return new Vector().elements(); - } - - /** - * {@inheritDoc} - */ - public Enumeration getCurrentCategories() { - return getCurrentLoggers(); - } - - - /** - * {@inheritDoc} - */ - public void fireAddAppenderEvent(Category logger, Appender appender) { - } - - /** - * {@inheritDoc} - */ - public void resetConfiguration() { - } -} diff --git a/java/src/org/apache/log4j/spi/NullWriter.java b/java/src/org/apache/log4j/spi/NullWriter.java deleted file mode 100644 index a578910..0000000 --- a/java/src/org/apache/log4j/spi/NullWriter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.spi; -import java.io.Writer; - -/** - * NullWriter is an obsolete class provided only for - * binary compatibility with earlier versions of log4j and should not be used. - * - * @deprecated - */ -class NullWriter extends Writer { - - public void close() { - // blank - } - - public void flush() { - // blank - } - - public void write(char[] cbuf, int off, int len) { - // blank - } -} diff --git a/java/src/org/apache/log4j/spi/OptionHandler.java b/java/src/org/apache/log4j/spi/OptionHandler.java deleted file mode 100644 index 2c90226..0000000 --- a/java/src/org/apache/log4j/spi/OptionHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - - -/** - A string based interface to configure package components. - - @author Ceki Gülcü - @author Anders Kristensen - @since 0.8.1 - */ -public interface OptionHandler { - - /** - Activate the options that were previously set with calls to option - setters. - -

This allows to defer activiation of the options until all - options have been set. This is required for components which have - related options that remain ambigous until all are set. - -

For example, the FileAppender has the {@link - org.apache.log4j.FileAppender#setFile File} and {@link - org.apache.log4j.FileAppender#setAppend Append} options both of - which are ambigous until the other is also set. */ - void activateOptions(); - - /** - Return list of strings that the OptionHandler instance recognizes. - - @deprecated We now use JavaBeans style getters/setters. - */ - // String[] getOptionStrings(); - - /** - Set option to value. - -

The handling of each option depends on the OptionHandler - instance. Some options may become active immediately whereas - other may be activated only when {@link #activateOptions} is - called. - - @deprecated We now use JavaBeans style getters/setters. - */ - //void setOption(String option, String value); -} diff --git a/java/src/org/apache/log4j/spi/RendererSupport.java b/java/src/org/apache/log4j/spi/RendererSupport.java deleted file mode 100644 index 9d69faa..0000000 --- a/java/src/org/apache/log4j/spi/RendererSupport.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package org.apache.log4j.spi; - -import org.apache.log4j.or.ObjectRenderer; -import org.apache.log4j.or.RendererMap; - - -public interface RendererSupport { - - public - RendererMap getRendererMap(); - - public - void setRenderer(Class renderedClass, ObjectRenderer renderer); - -} diff --git a/java/src/org/apache/log4j/spi/RepositorySelector.java b/java/src/org/apache/log4j/spi/RepositorySelector.java deleted file mode 100644 index 9a70d62..0000000 --- a/java/src/org/apache/log4j/spi/RepositorySelector.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - - -package org.apache.log4j.spi; - - -/** - - The LogManager uses one (and only one) - RepositorySelector implementation to select the - {@link LoggerRepository} for a particular application context. - -

It is the responsability of the RepositorySelector - implementation to track the application context. Log4j makes no - assumptions about the application context or on its management. - -

See also {@link org.apache.log4j.LogManager LogManager}. - - @author Ceki Gülcü - @since 1.2 - - */ -public interface RepositorySelector { - - /** - Returns a {@link LoggerRepository} depending on the - context. Implementors must make sure that a valid (non-null) - LoggerRepository is returned. - */ - public - LoggerRepository getLoggerRepository(); -} - diff --git a/java/src/org/apache/log4j/spi/RootCategory.java b/java/src/org/apache/log4j/spi/RootCategory.java deleted file mode 100644 index 9954791..0000000 --- a/java/src/org/apache/log4j/spi/RootCategory.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.helpers.LogLog; - -// Contibutors: Mathias Bogaert - -/** - * @deprecated Replaced by {@link RootLogger}. - */ -final public class RootCategory extends Logger { - - /** - The root category names itself as "root". However, the root - category cannot be retrieved by name. - */ - public - RootCategory(Level level) { - super("root"); - setLevel(level); - } - - - /** - Return the assigned level value without walking the category - hierarchy. - */ - final - public - Level getChainedLevel() { - return level; - } - - /** - Setting a null value to the level of the root category may have catastrophic - results. We prevent this here. - - @since 0.8.3 */ - final - public - void setLevel(Level level) { - if(level == null) { - LogLog.error("You have tried to set a null level to root.", - new Throwable()); - } - else { - this.level = level; - } - } - - final - public - void setPriority(Level level) { - setLevel(level); - } - - -} diff --git a/java/src/org/apache/log4j/spi/RootLogger.java b/java/src/org/apache/log4j/spi/RootLogger.java deleted file mode 100644 index a9ffd74..0000000 --- a/java/src/org/apache/log4j/spi/RootLogger.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.helpers.LogLog; - - -// Contibutors: Mathias Bogaert - -/** - RootLogger sits at the top of the logger hierachy. It is a - regular logger except that it provides several guarantees. - -

First, it cannot be assigned a null - level. Second, since root logger cannot have a parent, the - {@link #getChainedLevel} method always returns the value of the - level field without walking the hierarchy. - - @author Ceki Gülcü - - */ -public final class RootLogger extends Logger { - /** - The root logger names itself as "root". However, the root - logger cannot be retrieved by name. - */ - public RootLogger(Level level) { - super("root"); - setLevel(level); - } - - /** - Return the assigned level value without walking the logger - hierarchy. - */ - public final Level getChainedLevel() { - return level; - } - - /** - Setting a null value to the level of the root logger may have catastrophic - results. We prevent this here. - - @since 0.8.3 */ - public final void setLevel(Level level) { - if (level == null) { - LogLog.error( - "You have tried to set a null level to root.", new Throwable()); - } else { - this.level = level; - } - } - -} diff --git a/java/src/org/apache/log4j/spi/ThrowableInformation.java b/java/src/org/apache/log4j/spi/ThrowableInformation.java deleted file mode 100644 index 033f18b..0000000 --- a/java/src/org/apache/log4j/spi/ThrowableInformation.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -import org.apache.log4j.Category; -import org.apache.log4j.DefaultThrowableRenderer; - -/** - * ThrowableInformation is log4j's internal representation of - * throwables. It essentially consists of a string array, called - * 'rep', where the first element, that is rep[0], represents the - * string representation of the throwable (i.e. the value you get - * when you do throwable.toString()) and subsequent elements - * correspond the stack trace with the top most entry of the stack - * corresponding to the second entry of the 'rep' array that is - * rep[1]. - * - * @author Ceki Gülcü - * - * */ -public class ThrowableInformation implements java.io.Serializable { - - static final long serialVersionUID = -4748765566864322735L; - - private transient Throwable throwable; - private transient Category category; - private String[] rep; - - public - ThrowableInformation(Throwable throwable) { - this.throwable = throwable; - } - - /** - * Create a new instance. - * @param throwable throwable, may not be null. - * @param category category used to obtain ThrowableRenderer, may be null. - * @since 1.2.16 - */ - public ThrowableInformation(Throwable throwable, Category category) { - this.throwable = throwable; - this.category = category; - } - - /** - * Create new instance. - * @since 1.2.15 - * @param r String representation of throwable. - */ - public ThrowableInformation(final String[] r) { - if (r != null) { - rep = (String[]) r.clone(); - } - } - - - public - Throwable getThrowable() { - return throwable; - } - - public synchronized String[] getThrowableStrRep() { - if(rep == null) { - ThrowableRenderer renderer = null; - if (category != null) { - LoggerRepository repo = category.getLoggerRepository(); - if (repo instanceof ThrowableRendererSupport) { - renderer = ((ThrowableRendererSupport) repo).getThrowableRenderer(); - } - } - if (renderer == null) { - rep = DefaultThrowableRenderer.render(throwable); - } else { - rep = renderer.doRender(throwable); - } - } - return (String[]) rep.clone(); - } -} - - diff --git a/java/src/org/apache/log4j/spi/ThrowableRenderer.java b/java/src/org/apache/log4j/spi/ThrowableRenderer.java deleted file mode 100644 index 1b83db2..0000000 --- a/java/src/org/apache/log4j/spi/ThrowableRenderer.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.spi; - -/** - * Implemented by classes that render instances of - * java.lang.Throwable (exceptions and errors) - * into a string representation. - * - * @since 1.2.16 - */ -public interface ThrowableRenderer { - /** - * Render Throwable. - * @param t throwable, may not be null. - * @return String representation. - */ - public String[] doRender(Throwable t); -} diff --git a/java/src/org/apache/log4j/spi/ThrowableRendererSupport.java b/java/src/org/apache/log4j/spi/ThrowableRendererSupport.java deleted file mode 100644 index 08f02a0..0000000 --- a/java/src/org/apache/log4j/spi/ThrowableRendererSupport.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.spi; - -/** - * Implemented by logger repositories that support configurable - * rendering of Throwables. - * - * @since 1.2.16 - */ -public interface ThrowableRendererSupport { - /** - * Get throwable renderer. - * @return throwable renderer, may be null. - */ - ThrowableRenderer getThrowableRenderer(); - - /** - * Set throwable renderer. - * @param renderer renderer, may be null. - */ - void setThrowableRenderer(ThrowableRenderer renderer); -} diff --git a/java/src/org/apache/log4j/spi/TriggeringEventEvaluator.java b/java/src/org/apache/log4j/spi/TriggeringEventEvaluator.java deleted file mode 100644 index 67f4fcd..0000000 --- a/java/src/org/apache/log4j/spi/TriggeringEventEvaluator.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.spi; - -/** - - Implementions of this interface allow certain appenders to decide - when to perform an appender specific action. - -

For example the {@link org.apache.log4j.net.SMTPAppender} sends - an email when the {@link #isTriggeringEvent} method returns - true and adds the event to an internal buffer when the - returned result is false. - - @author Ceki Gülcü - @since version 1.0 - - */ -public interface TriggeringEventEvaluator { - - /** - Is this the triggering event? - */ - public boolean isTriggeringEvent(LoggingEvent event); -} diff --git a/java/src/org/apache/log4j/spi/VectorWriter.java b/java/src/org/apache/log4j/spi/VectorWriter.java deleted file mode 100644 index 3e28860..0000000 --- a/java/src/org/apache/log4j/spi/VectorWriter.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.spi; - -import java.io.PrintWriter; -import java.util.Vector; - -/** - * VectorWriter is an obsolete class provided only for - * binary compatibility with earlier versions of log4j and should not be used. - * - * @deprecated - */ -class VectorWriter extends PrintWriter { - - private Vector v; - - /** - * @deprecated - */ - VectorWriter() { - super(new NullWriter()); - v = new Vector(); - } - - public void print(Object o) { - v.addElement(String.valueOf(o)); - } - - public void print(char[] chars) { - v.addElement(new String(chars)); - } - - public void print(String s) { - v.addElement(s); - } - - public void println(Object o) { - v.addElement(String.valueOf(o)); - } - - // JDK 1.1.x apprenly uses this form of println while in - // printStackTrace() - public - void println(char[] chars) { - v.addElement(new String(chars)); - } - - public - void println(String s) { - v.addElement(s); - } - - public void write(char[] chars) { - v.addElement(new String(chars)); - } - - public void write(char[] chars, int off, int len) { - v.addElement(new String(chars, off, len)); - } - - public void write(String s, int off, int len) { - v.addElement(s.substring(off, off+len)); - } - - public void write(String s) { - v.addElement(s); - } - - public String[] toStringArray() { - int len = v.size(); - String[] sa = new String[len]; - for(int i = 0; i < len; i++) { - sa[i] = (String) v.elementAt(i); - } - return sa; - } - -} - diff --git a/java/src/org/apache/log4j/spi/package.html b/java/src/org/apache/log4j/spi/package.html deleted file mode 100644 index 1205833..0000000 --- a/java/src/org/apache/log4j/spi/package.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - -Contains part of the System Programming Interface (SPI) needed to -extend log4j. - - - - \ No newline at end of file diff --git a/java/src/org/apache/log4j/varia/DenyAllFilter.java b/java/src/org/apache/log4j/varia/DenyAllFilter.java deleted file mode 100644 index 6c9e949..0000000 --- a/java/src/org/apache/log4j/varia/DenyAllFilter.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.varia; - -import org.apache.log4j.spi.Filter; -import org.apache.log4j.spi.LoggingEvent; - - -/** - This filter drops all logging events. - -

You can add this filter to the end of a filter chain to - switch from the default "accept all unless instructed otherwise" - filtering behaviour to a "deny all unless instructed otherwise" - behaviour. - - - @author Ceki Gülcü - - @since 0.9.0 */ -public class DenyAllFilter extends Filter { - - /** - Returns null as there are no options. - - @deprecated We now use JavaBeans introspection to configure - components. Options strings are no longer needed. - */ - public - String[] getOptionStrings() { - return null; - } - - - /** - No options to set. - - @deprecated Use the setter method for the option directly instead - of the generic setOption method. - */ - public - void setOption(String key, String value) { - } - - /** - Always returns the integer constant {@link Filter#DENY} - regardless of the {@link LoggingEvent} parameter. - - @param event The LoggingEvent to filter. - @return Always returns {@link Filter#DENY}. - */ - public - int decide(LoggingEvent event) { - return Filter.DENY; - } -} - diff --git a/java/src/org/apache/log4j/varia/ExternallyRolledFileAppender.java b/java/src/org/apache/log4j/varia/ExternallyRolledFileAppender.java deleted file mode 100644 index 26e7d84..0000000 --- a/java/src/org/apache/log4j/varia/ExternallyRolledFileAppender.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.varia; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.ServerSocket; -import java.net.Socket; - -import org.apache.log4j.RollingFileAppender; -import org.apache.log4j.helpers.LogLog; - -/** - This appender listens on a socket on the port specified by the - Port property for a "RollOver" message. When such a message - is received, the underlying log file is rolled over and an - acknowledgment message is sent back to the process initiating the - roll over. - -

This method of triggering roll over has the advantage of being - operating system independent, fast and reliable. - -

A simple application {@link Roller} is provided to initiate the - roll over. - -

Note that the initiator is not authenticated. Anyone can trigger - a rollover. In production environments, it is recommended that you - add some form of protection to prevent undesired rollovers. - - - @author Ceki Gülcü - @since version 0.9.0 */ -public class ExternallyRolledFileAppender extends RollingFileAppender { - - /** - The string constant sent to initiate a roll over. Current value of - this string constant is RollOver. - */ - static final public String ROLL_OVER = "RollOver"; - - /** - The string constant sent to acknowledge a roll over. Current value of - this string constant is OK. - */ - static final public String OK = "OK"; - - int port = 0; - HUP hup; - - /** - The default constructor does nothing but calls its super-class - constructor. */ - public - ExternallyRolledFileAppender() { - } - - /** - The Port [roperty is used for setting the port for - listening to external roll over messages. - */ - public - void setPort(int port) { - this.port = port; - } - - /** - Returns value of the Port option. - */ - public - int getPort() { - return port; - } - - /** - Start listening on the port specified by a preceding call to - {@link #setPort}. */ - public - void activateOptions() { - super.activateOptions(); - if(port != 0) { - if(hup != null) { - hup.interrupt(); - } - hup = new HUP(this, port); - hup.setDaemon(true); - hup.start(); - } - } -} - - -class HUP extends Thread { - - int port; - ExternallyRolledFileAppender er; - - HUP(ExternallyRolledFileAppender er, int port) { - this.er = er; - this.port = port; - } - - public - void run() { - while(!isInterrupted()) { - try { - ServerSocket serverSocket = new ServerSocket(port); - while(true) { - Socket socket = serverSocket.accept(); - LogLog.debug("Connected to client at " + socket.getInetAddress()); - new Thread(new HUPNode(socket, er), "ExternallyRolledFileAppender-HUP").start(); - } - } catch(InterruptedIOException e) { - Thread.currentThread().interrupt(); - e.printStackTrace(); - } catch(IOException e) { - e.printStackTrace(); - } catch(RuntimeException e) { - e.printStackTrace(); - } - } - } -} - -class HUPNode implements Runnable { - - Socket socket; - DataInputStream dis; - DataOutputStream dos; - ExternallyRolledFileAppender er; - - public - HUPNode(Socket socket, ExternallyRolledFileAppender er) { - this.socket = socket; - this.er = er; - try { - dis = new DataInputStream(socket.getInputStream()); - dos = new DataOutputStream(socket.getOutputStream()); - } catch(InterruptedIOException e) { - Thread.currentThread().interrupt(); - e.printStackTrace(); - } catch(IOException e) { - e.printStackTrace(); - } catch(RuntimeException e) { - e.printStackTrace(); - } - } - - public void run() { - try { - String line = dis.readUTF(); - LogLog.debug("Got external roll over signal."); - if(ExternallyRolledFileAppender.ROLL_OVER.equals(line)) { - synchronized(er) { - er.rollOver(); - } - dos.writeUTF(ExternallyRolledFileAppender.OK); - } - else { - dos.writeUTF("Expecting [RollOver] string."); - } - dos.close(); - } catch(InterruptedIOException e) { - Thread.currentThread().interrupt(); - LogLog.error("Unexpected exception. Exiting HUPNode.", e); - } catch(IOException e) { - LogLog.error("Unexpected exception. Exiting HUPNode.", e); - } catch(RuntimeException e) { - LogLog.error("Unexpected exception. Exiting HUPNode.", e); - } - } -} - diff --git a/java/src/org/apache/log4j/varia/FallbackErrorHandler.java b/java/src/org/apache/log4j/varia/FallbackErrorHandler.java deleted file mode 100644 index 7cbc87d..0000000 --- a/java/src/org/apache/log4j/varia/FallbackErrorHandler.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.varia; - -import org.apache.log4j.spi.ErrorHandler; -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.Appender; -import org.apache.log4j.Logger; -import org.apache.log4j.helpers.LogLog; -import java.util.Vector; -import java.io.InterruptedIOException; - -/** - * - * The FallbackErrorHandler implements the ErrorHandler - * interface such that a secondary appender may be specified. This - * secondary appender takes over if the primary appender fails for - * whatever reason. - * - *

The error message is printed on System.err, and - * logged in the new secondary appender. - * - * @author Ceki Gücü - * */ -public class FallbackErrorHandler implements ErrorHandler { - - - Appender backup; - Appender primary; - Vector loggers; - - public FallbackErrorHandler() { - } - - - /** - Adds the logger passed as parameter to the list of - loggers that we need to search for in case of appender failure. - */ - public - void setLogger(Logger logger) { - LogLog.debug("FB: Adding logger [" + logger.getName() + "]."); - if(loggers == null) { - loggers = new Vector(); - } - loggers.addElement(logger); - } - - - /** - No options to activate. - */ - public - void activateOptions() { - } - - - /** - Prints the message and the stack trace of the exception on - System.err. */ - public - void error(String message, Exception e, int errorCode) { - error(message, e, errorCode, null); - } - - /** - Prints the message and the stack trace of the exception on - System.err. - */ - public - void error(String message, Exception e, int errorCode, LoggingEvent event) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.debug("FB: The following error reported: " + message, e); - LogLog.debug("FB: INITIATING FALLBACK PROCEDURE."); - if (loggers != null) { - for(int i = 0; i < loggers.size(); i++) { - Logger l = (Logger) loggers.elementAt(i); - LogLog.debug("FB: Searching for ["+primary.getName()+"] in logger [" - +l.getName() + "]."); - LogLog.debug("FB: Replacing ["+primary.getName()+"] by [" - + backup.getName() + "] in logger ["+ l.getName() +"]."); - l.removeAppender(primary); - LogLog.debug("FB: Adding appender ["+backup.getName()+"] to logger " - + l.getName()); - l.addAppender(backup); - } - } - } - - - /** - Print a the error message passed as parameter on - System.err. - */ - public - void error(String message) { - //if(firstTime) { - //LogLog.error(message); - //firstTime = false; - //} - } - - /** - The appender to which this error handler is attached. - */ - public - void setAppender(Appender primary) { - LogLog.debug("FB: Setting primary appender to [" + primary.getName() + "]."); - this.primary = primary; - } - - /** - Set the backup appender. - */ - public - void setBackupAppender(Appender backup) { - LogLog.debug("FB: Setting backup appender to [" + backup.getName() + "]."); - this.backup = backup; - } - -} diff --git a/java/src/org/apache/log4j/varia/LevelMatchFilter.java b/java/src/org/apache/log4j/varia/LevelMatchFilter.java deleted file mode 100644 index 832ca29..0000000 --- a/java/src/org/apache/log4j/varia/LevelMatchFilter.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.varia; - -import org.apache.log4j.Level; -import org.apache.log4j.spi.Filter; -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.helpers.OptionConverter; - -/** - This is a very simple filter based on level matching. - -

The filter admits two options LevelToMatch and - AcceptOnMatch. If there is an exact match between the value - of the LevelToMatch option and the level of the {@link - LoggingEvent}, then the {@link #decide} method returns {@link - Filter#ACCEPT} in case the AcceptOnMatch option value is set - to true, if it is false then {@link - Filter#DENY} is returned. If there is no match, {@link - Filter#NEUTRAL} is returned. - - @author Ceki Gülcü - - @since 1.2 */ -public class LevelMatchFilter extends Filter { - - /** - Do we return ACCEPT when a match occurs. Default is - true. */ - boolean acceptOnMatch = true; - - /** - */ - Level levelToMatch; - - - public - void setLevelToMatch(String level) { - levelToMatch = OptionConverter.toLevel(level, null); - } - - public - String getLevelToMatch() { - return levelToMatch == null ? null : levelToMatch.toString(); - } - - public - void setAcceptOnMatch(boolean acceptOnMatch) { - this.acceptOnMatch = acceptOnMatch; - } - - public - boolean getAcceptOnMatch() { - return acceptOnMatch; - } - - - /** - Return the decision of this filter. - - Returns {@link Filter#NEUTRAL} if the LevelToMatch option - is not set or if there is not match. Otherwise, if there is a - match, then the returned decision is {@link Filter#ACCEPT} if the - AcceptOnMatch property is set to true. The - returned decision is {@link Filter#DENY} if the - AcceptOnMatch property is set to false. - - */ - public - int decide(LoggingEvent event) { - if(this.levelToMatch == null) { - return Filter.NEUTRAL; - } - - boolean matchOccured = false; - if(this.levelToMatch.equals(event.getLevel())) { - matchOccured = true; - } - - if(matchOccured) { - if(this.acceptOnMatch) - return Filter.ACCEPT; - else - return Filter.DENY; - } else { - return Filter.NEUTRAL; - } - } -} diff --git a/java/src/org/apache/log4j/varia/LevelRangeFilter.java b/java/src/org/apache/log4j/varia/LevelRangeFilter.java deleted file mode 100644 index 40994d3..0000000 --- a/java/src/org/apache/log4j/varia/LevelRangeFilter.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.varia; - -import org.apache.log4j.Level; -import org.apache.log4j.spi.Filter; -import org.apache.log4j.spi.LoggingEvent; - -/** - This is a very simple filter based on level matching, which can be - used to reject messages with priorities outside a certain range. - -

The filter admits three options LevelMin, LevelMax - and AcceptOnMatch. - -

If the level of the {@link LoggingEvent} is not between Min and Max - (inclusive), then {@link Filter#DENY} is returned. - -

If the Logging event level is within the specified range, then if - AcceptOnMatch is true, {@link Filter#ACCEPT} is returned, and if - AcceptOnMatch is false, {@link Filter#NEUTRAL} is returned. - -

If LevelMinw is not defined, then there is no - minimum acceptable level (ie a level is never rejected for - being too "low"/unimportant). If LevelMax is not - defined, then there is no maximum acceptable level (ie a - level is never rejected for beeing too "high"/important). - -

Refer to the {@link - org.apache.log4j.AppenderSkeleton#setThreshold setThreshold} method - available to all appenders extending {@link - org.apache.log4j.AppenderSkeleton} for a more convenient way to - filter out events by level. - - @author Simon Kitching - @author based on code by Ceki Gülcü -*/ -public class LevelRangeFilter extends Filter { - - /** - Do we return ACCEPT when a match occurs. Default is - false, so that later filters get running by default */ - boolean acceptOnMatch = false; - - Level levelMin; - Level levelMax; - - - /** - Return the decision of this filter. - */ - public - int decide(LoggingEvent event) { - if(this.levelMin != null) { - if (event.getLevel().isGreaterOrEqual(levelMin) == false) { - // level of event is less than minimum - return Filter.DENY; - } - } - - if(this.levelMax != null) { - if (event.getLevel().toInt() > levelMax.toInt()) { - // level of event is greater than maximum - // Alas, there is no Level.isGreater method. and using - // a combo of isGreaterOrEqual && !Equal seems worse than - // checking the int values of the level objects.. - return Filter.DENY; - } - } - - if (acceptOnMatch) { - // this filter set up to bypass later filters and always return - // accept if level in range - return Filter.ACCEPT; - } - else { - // event is ok for this filter; allow later filters to have a look.. - return Filter.NEUTRAL; - } - } - - /** - Get the value of the LevelMax option. */ - public - Level getLevelMax() { - return levelMax; - } - - - /** - Get the value of the LevelMin option. */ - public - Level getLevelMin() { - return levelMin; - } - - /** - Get the value of the AcceptOnMatch option. - */ - public - boolean getAcceptOnMatch() { - return acceptOnMatch; - } - - /** - Set the LevelMax option. - */ - public - void setLevelMax(Level levelMax) { - this.levelMax = levelMax; - } - - /** - Set the LevelMin option. - */ - public - void setLevelMin(Level levelMin) { - this.levelMin = levelMin; - } - - /** - Set the AcceptOnMatch option. - */ - public - void setAcceptOnMatch(boolean acceptOnMatch) { - this.acceptOnMatch = acceptOnMatch; - } -} - diff --git a/java/src/org/apache/log4j/varia/NullAppender.java b/java/src/org/apache/log4j/varia/NullAppender.java deleted file mode 100644 index 778b024..0000000 --- a/java/src/org/apache/log4j/varia/NullAppender.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.varia; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.spi.LoggingEvent; - -/** - * A NullAppender merely exists, it never outputs a message to any - * device. - * @author Ceki Gülc¨ - */ -public class NullAppender extends AppenderSkeleton { - - private static NullAppender instance = new NullAppender(); - - public NullAppender() { - } - - /** - * There are no options to acticate. - * */ - public void activateOptions() { - } - - /** - * Whenever you can, use this method to retreive an instance instead - * of instantiating a new one with new. - * @deprecated Use getNullAppender instead. getInstance should have been static. - * */ - public NullAppender getInstance() { - return instance; - } - - /** - * Whenever you can, use this method to retreive an instance instead - * of instantiating a new one with new. - * */ - public static NullAppender getNullAppender() { - return instance; - } - - public void close() { - } - - /** - * Does not do anything. - * */ - public void doAppend(LoggingEvent event) { - } - - /** - * Does not do anything. - * */ - protected void append(LoggingEvent event) { - } - - /** - * NullAppenders do not need a layout. - * */ - public boolean requiresLayout() { - return false; - } -} diff --git a/java/src/org/apache/log4j/varia/ReloadingPropertyConfigurator.java b/java/src/org/apache/log4j/varia/ReloadingPropertyConfigurator.java deleted file mode 100644 index b29e847..0000000 --- a/java/src/org/apache/log4j/varia/ReloadingPropertyConfigurator.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.varia; - -import org.apache.log4j.PropertyConfigurator; -import org.apache.log4j.spi.Configurator; -import java.net.URL; -import org.apache.log4j.spi.LoggerRepository; - -public class ReloadingPropertyConfigurator implements Configurator { - - - PropertyConfigurator delegate = new PropertyConfigurator(); - - - public ReloadingPropertyConfigurator() { - } - - public - void doConfigure(URL url, LoggerRepository repository) { - } - -} diff --git a/java/src/org/apache/log4j/varia/Roller.java b/java/src/org/apache/log4j/varia/Roller.java deleted file mode 100644 index 7ef3aee..0000000 --- a/java/src/org/apache/log4j/varia/Roller.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.varia; - -import org.apache.log4j.Logger; -import org.apache.log4j.BasicConfigurator; - -import java.io.IOException; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.net.Socket; - -/** - A simple application to send roll over messages to a potentially - remote {@link ExternallyRolledFileAppender}. - -

It takes two arguments, the host_name and - port_number where the - ExternallyRolledFileAppender is listening. - - - @author Ceki Gülcü - @since version 0.9.0 */ -public class Roller { - - static Logger cat = Logger.getLogger(Roller.class); - - - static String host; - static int port; - - // Static class. - Roller() { - } - - /** - Send a "RollOver" message to - ExternallyRolledFileAppender on host - and port. - - */ - public - static - void main(String argv[]) { - - BasicConfigurator.configure(); - - if(argv.length == 2) - init(argv[0], argv[1]); - else - usage("Wrong number of arguments."); - - roll(); - } - - static - void usage(String msg) { - System.err.println(msg); - System.err.println( "Usage: java " + Roller.class.getName() + - "host_name port_number"); - System.exit(1); - } - - static - void init(String hostArg, String portArg) { - host = hostArg; - try { - port = Integer.parseInt(portArg); - } - catch(java.lang.NumberFormatException e) { - usage("Second argument "+portArg+" is not a valid integer."); - } - } - - static - void roll() { - try { - Socket socket = new Socket(host, port); - DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); - DataInputStream dis = new DataInputStream(socket.getInputStream()); - dos.writeUTF(ExternallyRolledFileAppender.ROLL_OVER); - String rc = dis.readUTF(); - if(ExternallyRolledFileAppender.OK.equals(rc)) { - cat.info("Roll over signal acknowledged by remote appender."); - } else { - cat.warn("Unexpected return code "+rc+" from remote entity."); - System.exit(2); - } - } catch(IOException e) { - cat.error("Could not send roll signal on host "+host+" port "+port+" .", - e); - System.exit(2); - } - System.exit(0); - } -} diff --git a/java/src/org/apache/log4j/varia/StringMatchFilter.java b/java/src/org/apache/log4j/varia/StringMatchFilter.java deleted file mode 100644 index 6ee7ec4..0000000 --- a/java/src/org/apache/log4j/varia/StringMatchFilter.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.varia; - -import org.apache.log4j.spi.Filter; -import org.apache.log4j.spi.LoggingEvent; -import org.apache.log4j.helpers.OptionConverter; - -/** - * This is a very simple filter based on string matching. - * - *

The filter admits two options StringToMatch and - * AcceptOnMatch. If there is a match between the value of the - * StringToMatch option and the message of the {@link org.apache.log4j.spi.LoggingEvent}, - * then the {@link #decide(LoggingEvent)} method returns {@link org.apache.log4j.spi.Filter#ACCEPT} if - * the AcceptOnMatch option value is true, if it is false then - * {@link org.apache.log4j.spi.Filter#DENY} is returned. If there is no match, {@link - * org.apache.log4j.spi.Filter#NEUTRAL} is returned. - * - * @author Ceki Gülcü - * @since 0.9.0 - */ -public class StringMatchFilter extends Filter { - - /** - @deprecated Options are now handled using the JavaBeans paradigm. - This constant is not longer needed and will be removed in the - near term. - */ - public static final String STRING_TO_MATCH_OPTION = "StringToMatch"; - - /** - @deprecated Options are now handled using the JavaBeans paradigm. - This constant is not longer needed and will be removed in the - near term. - */ - public static final String ACCEPT_ON_MATCH_OPTION = "AcceptOnMatch"; - - boolean acceptOnMatch = true; - String stringToMatch; - - /** - @deprecated We now use JavaBeans introspection to configure - components. Options strings are no longer needed. - */ - public - String[] getOptionStrings() { - return new String[] {STRING_TO_MATCH_OPTION, ACCEPT_ON_MATCH_OPTION}; - } - - /** - @deprecated Use the setter method for the option directly instead - of the generic setOption method. - */ - public - void setOption(String key, String value) { - - if(key.equalsIgnoreCase(STRING_TO_MATCH_OPTION)) { - stringToMatch = value; - } else if (key.equalsIgnoreCase(ACCEPT_ON_MATCH_OPTION)) { - acceptOnMatch = OptionConverter.toBoolean(value, acceptOnMatch); - } - } - - public - void setStringToMatch(String s) { - stringToMatch = s; - } - - public - String getStringToMatch() { - return stringToMatch; - } - - public - void setAcceptOnMatch(boolean acceptOnMatch) { - this.acceptOnMatch = acceptOnMatch; - } - - public - boolean getAcceptOnMatch() { - return acceptOnMatch; - } - - /** - Returns {@link Filter#NEUTRAL} is there is no string match. - */ - public - int decide(LoggingEvent event) { - String msg = event.getRenderedMessage(); - - if(msg == null || stringToMatch == null) - return Filter.NEUTRAL; - - - if( msg.indexOf(stringToMatch) == -1 ) { - return Filter.NEUTRAL; - } else { // we've got a match - if(acceptOnMatch) { - return Filter.ACCEPT; - } else { - return Filter.DENY; - } - } - } -} diff --git a/java/src/org/apache/log4j/varia/package.html b/java/src/org/apache/log4j/varia/package.html deleted file mode 100644 index 3495db2..0000000 --- a/java/src/org/apache/log4j/varia/package.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - -

Contains various appenders, filters and other odds and ends. - -


-
- -Last modified: Tue Mar 21 20:28:14 MET 2000 - - diff --git a/java/src/org/apache/log4j/xml/DOMConfigurator.java b/java/src/org/apache/log4j/xml/DOMConfigurator.java deleted file mode 100644 index de2963d..0000000 --- a/java/src/org/apache/log4j/xml/DOMConfigurator.java +++ /dev/null @@ -1,1123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.xml; - -import org.apache.log4j.Appender; -import org.apache.log4j.Layout; -import org.apache.log4j.Level; -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; -import org.apache.log4j.config.PropertySetter; -import org.apache.log4j.helpers.FileWatchdog; -import org.apache.log4j.helpers.Loader; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.helpers.OptionConverter; -import org.apache.log4j.or.RendererMap; -import org.apache.log4j.spi.AppenderAttachable; -import org.apache.log4j.spi.Configurator; -import org.apache.log4j.spi.ErrorHandler; -import org.apache.log4j.spi.Filter; -import org.apache.log4j.spi.LoggerFactory; -import org.apache.log4j.spi.LoggerRepository; -import org.apache.log4j.spi.RendererSupport; -import org.apache.log4j.spi.ThrowableRenderer; -import org.apache.log4j.spi.ThrowableRendererSupport; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.FactoryConfigurationError; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InterruptedIOException; -import java.io.Reader; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; -import java.net.URL; -import java.net.URLConnection; -import java.util.Hashtable; -import java.util.Properties; - -// Contributors: Mark Womack -// Arun Katkere - -/** - Use this class to initialize the log4j environment using a DOM tree. - -

The DTD is specified in log4j.dtd. - -

Sometimes it is useful to see how log4j is reading configuration - files. You can enable log4j internal logging by defining the - log4j.debug variable on the java command - line. Alternatively, set the debug attribute in the - log4j:configuration element. As in -

-   <log4j:configuration debug="true" xmlns:log4j="http://jakarta.apache.org/log4j/">
-   ...
-   </log4j:configuration>
-
- -

There are sample XML files included in the package. - - @author Christopher Taylor - @author Ceki Gülcü - @author Anders Kristensen - - @since 0.8.3 */ -public class DOMConfigurator implements Configurator { - - static final String CONFIGURATION_TAG = "log4j:configuration"; - static final String OLD_CONFIGURATION_TAG = "configuration"; - static final String RENDERER_TAG = "renderer"; - private static final String THROWABLE_RENDERER_TAG = "throwableRenderer"; - static final String APPENDER_TAG = "appender"; - static final String APPENDER_REF_TAG = "appender-ref"; - static final String PARAM_TAG = "param"; - static final String LAYOUT_TAG = "layout"; - static final String CATEGORY = "category"; - static final String LOGGER = "logger"; - static final String LOGGER_REF = "logger-ref"; - static final String CATEGORY_FACTORY_TAG = "categoryFactory"; - static final String LOGGER_FACTORY_TAG = "loggerFactory"; - static final String NAME_ATTR = "name"; - static final String CLASS_ATTR = "class"; - static final String VALUE_ATTR = "value"; - static final String ROOT_TAG = "root"; - static final String ROOT_REF = "root-ref"; - static final String LEVEL_TAG = "level"; - static final String PRIORITY_TAG = "priority"; - static final String FILTER_TAG = "filter"; - static final String ERROR_HANDLER_TAG = "errorHandler"; - static final String REF_ATTR = "ref"; - static final String ADDITIVITY_ATTR = "additivity"; - static final String THRESHOLD_ATTR = "threshold"; - static final String CONFIG_DEBUG_ATTR = "configDebug"; - static final String INTERNAL_DEBUG_ATTR = "debug"; - private static final String RESET_ATTR = "reset"; - static final String RENDERING_CLASS_ATTR = "renderingClass"; - static final String RENDERED_CLASS_ATTR = "renderedClass"; - - static final String EMPTY_STR = ""; - static final Class[] ONE_STRING_PARAM = new Class[] {String.class}; - - final static String dbfKey = "javax.xml.parsers.DocumentBuilderFactory"; - - - // key: appenderName, value: appender - Hashtable appenderBag; - - Properties props; - LoggerRepository repository; - - protected LoggerFactory catFactory = null; - - /** - No argument constructor. - */ - public - DOMConfigurator () { - appenderBag = new Hashtable(); - } - - /** - Used internally to parse appenders by IDREF name. - */ - protected - Appender findAppenderByName(Document doc, String appenderName) { - Appender appender = (Appender) appenderBag.get(appenderName); - - if(appender != null) { - return appender; - } else { - // Doesn't work on DOM Level 1 : - // Element element = doc.getElementById(appenderName); - - // Endre's hack: - Element element = null; - NodeList list = doc.getElementsByTagName("appender"); - for (int t=0; t < list.getLength(); t++) { - Node node = list.item(t); - NamedNodeMap map= node.getAttributes(); - Node attrNode = map.getNamedItem("name"); - if (appenderName.equals(attrNode.getNodeValue())) { - element = (Element) node; - break; - } - } - // Hack finished. - - if(element == null) { - LogLog.error("No appender named ["+appenderName+"] could be found."); - return null; - } else { - appender = parseAppender(element); - if (appender != null) { - appenderBag.put(appenderName, appender); - } - return appender; - } - } - } - /** - Used internally to parse appenders by IDREF element. - */ - protected - Appender findAppenderByReference(Element appenderRef) { - String appenderName = subst(appenderRef.getAttribute(REF_ATTR)); - Document doc = appenderRef.getOwnerDocument(); - return findAppenderByName(doc, appenderName); - } - - /** - * Delegates unrecognized content to created instance if - * it supports UnrecognizedElementParser. - * @since 1.2.15 - * @param instance instance, may be null. - * @param element element, may not be null. - * @param props properties - * @throws IOException thrown if configuration of owner object - * should be abandoned. - */ - private static void parseUnrecognizedElement(final Object instance, - final Element element, - final Properties props) throws Exception { - boolean recognized = false; - if (instance instanceof UnrecognizedElementHandler) { - recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement( - element, props); - } - if (!recognized) { - LogLog.warn("Unrecognized element " + element.getNodeName()); - } - } - - /** - * Delegates unrecognized content to created instance if - * it supports UnrecognizedElementParser and catches and - * logs any exception. - * @since 1.2.15 - * @param instance instance, may be null. - * @param element element, may not be null. - * @param props properties - */ - private static void quietParseUnrecognizedElement(final Object instance, - final Element element, - final Properties props) { - try { - parseUnrecognizedElement(instance, element, props); - } catch (Exception ex) { - if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("Error in extension content: ", ex); - } - } - - /** - Used internally to parse an appender element. - */ - protected - Appender parseAppender (Element appenderElement) { - String className = subst(appenderElement.getAttribute(CLASS_ATTR)); - LogLog.debug("Class name: [" + className+']'); - try { - Object instance = Loader.loadClass(className).newInstance(); - Appender appender = (Appender)instance; - PropertySetter propSetter = new PropertySetter(appender); - - appender.setName(subst(appenderElement.getAttribute(NAME_ATTR))); - - NodeList children = appenderElement.getChildNodes(); - final int length = children.getLength(); - - for (int loop = 0; loop < length; loop++) { - Node currentNode = children.item(loop); - - /* We're only interested in Elements */ - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - Element currentElement = (Element)currentNode; - - // Parse appender parameters - if (currentElement.getTagName().equals(PARAM_TAG)) { - setParameter(currentElement, propSetter); - } - // Set appender layout - else if (currentElement.getTagName().equals(LAYOUT_TAG)) { - appender.setLayout(parseLayout(currentElement)); - } - // Add filters - else if (currentElement.getTagName().equals(FILTER_TAG)) { - parseFilters(currentElement, appender); - } - else if (currentElement.getTagName().equals(ERROR_HANDLER_TAG)) { - parseErrorHandler(currentElement, appender); - } - else if (currentElement.getTagName().equals(APPENDER_REF_TAG)) { - String refName = subst(currentElement.getAttribute(REF_ATTR)); - if(appender instanceof AppenderAttachable) { - AppenderAttachable aa = (AppenderAttachable) appender; - LogLog.debug("Attaching appender named ["+ refName+ - "] to appender named ["+ appender.getName()+"]."); - aa.addAppender(findAppenderByReference(currentElement)); - } else { - LogLog.error("Requesting attachment of appender named ["+ - refName+ "] to appender named ["+ appender.getName()+ - "] which does not implement org.apache.log4j.spi.AppenderAttachable."); - } - } else { - parseUnrecognizedElement(instance, currentElement, props); - } - } - } - propSetter.activate(); - return appender; - } - /* Yes, it's ugly. But all of these exceptions point to the same - problem: we can't create an Appender */ - catch (Exception oops) { - if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("Could not create an Appender. Reported error follows.", - oops); - return null; - } - } - - /** - Used internally to parse an {@link ErrorHandler} element. - */ - protected - void parseErrorHandler(Element element, Appender appender) { - ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName( - subst(element.getAttribute(CLASS_ATTR)), - org.apache.log4j.spi.ErrorHandler.class, - null); - - if(eh != null) { - eh.setAppender(appender); - - PropertySetter propSetter = new PropertySetter(eh); - NodeList children = element.getChildNodes(); - final int length = children.getLength(); - - for (int loop = 0; loop < length; loop++) { - Node currentNode = children.item(loop); - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - Element currentElement = (Element) currentNode; - String tagName = currentElement.getTagName(); - if(tagName.equals(PARAM_TAG)) { - setParameter(currentElement, propSetter); - } else if(tagName.equals(APPENDER_REF_TAG)) { - eh.setBackupAppender(findAppenderByReference(currentElement)); - } else if(tagName.equals(LOGGER_REF)) { - String loggerName = currentElement.getAttribute(REF_ATTR); - Logger logger = (catFactory == null) ? repository.getLogger(loggerName) - : repository.getLogger(loggerName, catFactory); - eh.setLogger(logger); - } else if(tagName.equals(ROOT_REF)) { - Logger root = repository.getRootLogger(); - eh.setLogger(root); - } else { - quietParseUnrecognizedElement(eh, currentElement, props); - } - } - } - propSetter.activate(); - appender.setErrorHandler(eh); - } - } - - /** - Used internally to parse a filter element. - */ - protected - void parseFilters(Element element, Appender appender) { - String clazz = subst(element.getAttribute(CLASS_ATTR)); - Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, - Filter.class, null); - - if(filter != null) { - PropertySetter propSetter = new PropertySetter(filter); - NodeList children = element.getChildNodes(); - final int length = children.getLength(); - - for (int loop = 0; loop < length; loop++) { - Node currentNode = children.item(loop); - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - Element currentElement = (Element) currentNode; - String tagName = currentElement.getTagName(); - if(tagName.equals(PARAM_TAG)) { - setParameter(currentElement, propSetter); - } else { - quietParseUnrecognizedElement(filter, currentElement, props); - } - } - } - propSetter.activate(); - LogLog.debug("Adding filter of type ["+filter.getClass() - +"] to appender named ["+appender.getName()+"]."); - appender.addFilter(filter); - } - } - - /** - Used internally to parse an category element. - */ - protected - void parseCategory (Element loggerElement) { - // Create a new org.apache.log4j.Category object from the element. - String catName = subst(loggerElement.getAttribute(NAME_ATTR)); - - Logger cat; - - String className = subst(loggerElement.getAttribute(CLASS_ATTR)); - - - if(EMPTY_STR.equals(className)) { - LogLog.debug("Retreiving an instance of org.apache.log4j.Logger."); - cat = (catFactory == null) ? repository.getLogger(catName) : repository.getLogger(catName, catFactory); - } - else { - LogLog.debug("Desired logger sub-class: ["+className+']'); - try { - Class clazz = Loader.loadClass(className); - Method getInstanceMethod = clazz.getMethod("getLogger", - ONE_STRING_PARAM); - cat = (Logger) getInstanceMethod.invoke(null, new Object[] {catName}); - } catch (InvocationTargetException oops) { - if (oops.getTargetException() instanceof InterruptedException - || oops.getTargetException() instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("Could not retrieve category ["+catName+ - "]. Reported error follows.", oops); - return; - } catch (Exception oops) { - LogLog.error("Could not retrieve category ["+catName+ - "]. Reported error follows.", oops); - return; - } - } - - // Setting up a category needs to be an atomic operation, in order - // to protect potential log operations while category - // configuration is in progress. - synchronized(cat) { - boolean additivity = OptionConverter.toBoolean( - subst(loggerElement.getAttribute(ADDITIVITY_ATTR)), - true); - - LogLog.debug("Setting ["+cat.getName()+"] additivity to ["+additivity+"]."); - cat.setAdditivity(additivity); - parseChildrenOfLoggerElement(loggerElement, cat, false); - } - } - - - /** - Used internally to parse the category factory element. - */ - protected - void parseCategoryFactory(Element factoryElement) { - String className = subst(factoryElement.getAttribute(CLASS_ATTR)); - - if(EMPTY_STR.equals(className)) { - LogLog.error("Category Factory tag " + CLASS_ATTR + " attribute not found."); - LogLog.debug("No Category Factory configured."); - } - else { - LogLog.debug("Desired category factory: ["+className+']'); - Object factory = OptionConverter.instantiateByClassName(className, - LoggerFactory.class, - null); - if (factory instanceof LoggerFactory) { - catFactory = (LoggerFactory) factory; - } else { - LogLog.error("Category Factory class " + className + " does not implement org.apache.log4j.LoggerFactory"); - } - PropertySetter propSetter = new PropertySetter(factory); - - Element currentElement = null; - Node currentNode = null; - NodeList children = factoryElement.getChildNodes(); - final int length = children.getLength(); - - for (int loop=0; loop < length; loop++) { - currentNode = children.item(loop); - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - currentElement = (Element)currentNode; - if (currentElement.getTagName().equals(PARAM_TAG)) { - setParameter(currentElement, propSetter); - } else { - quietParseUnrecognizedElement(factory, currentElement, props); - } - } - } - } - } - - - /** - Used internally to parse the roor category element. - */ - protected - void parseRoot (Element rootElement) { - Logger root = repository.getRootLogger(); - // category configuration needs to be atomic - synchronized(root) { - parseChildrenOfLoggerElement(rootElement, root, true); - } - } - - - /** - Used internally to parse the children of a category element. - */ - protected - void parseChildrenOfLoggerElement(Element catElement, - Logger cat, boolean isRoot) { - - PropertySetter propSetter = new PropertySetter(cat); - - // Remove all existing appenders from cat. They will be - // reconstructed if need be. - cat.removeAllAppenders(); - - - NodeList children = catElement.getChildNodes(); - final int length = children.getLength(); - - for (int loop = 0; loop < length; loop++) { - Node currentNode = children.item(loop); - - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - Element currentElement = (Element) currentNode; - String tagName = currentElement.getTagName(); - - if (tagName.equals(APPENDER_REF_TAG)) { - Element appenderRef = (Element) currentNode; - Appender appender = findAppenderByReference(appenderRef); - String refName = subst(appenderRef.getAttribute(REF_ATTR)); - if(appender != null) - LogLog.debug("Adding appender named ["+ refName+ - "] to category ["+cat.getName()+"]."); - else - LogLog.debug("Appender named ["+ refName + "] not found."); - - cat.addAppender(appender); - - } else if(tagName.equals(LEVEL_TAG)) { - parseLevel(currentElement, cat, isRoot); - } else if(tagName.equals(PRIORITY_TAG)) { - parseLevel(currentElement, cat, isRoot); - } else if(tagName.equals(PARAM_TAG)) { - setParameter(currentElement, propSetter); - } else { - quietParseUnrecognizedElement(cat, currentElement, props); - } - } - } - propSetter.activate(); - } - - /** - Used internally to parse a layout element. - */ - protected - Layout parseLayout (Element layout_element) { - String className = subst(layout_element.getAttribute(CLASS_ATTR)); - LogLog.debug("Parsing layout of class: \""+className+"\""); - try { - Object instance = Loader.loadClass(className).newInstance(); - Layout layout = (Layout)instance; - PropertySetter propSetter = new PropertySetter(layout); - - NodeList params = layout_element.getChildNodes(); - final int length = params.getLength(); - - for (int loop = 0; loop < length; loop++) { - Node currentNode = (Node)params.item(loop); - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - Element currentElement = (Element) currentNode; - String tagName = currentElement.getTagName(); - if(tagName.equals(PARAM_TAG)) { - setParameter(currentElement, propSetter); - } else { - parseUnrecognizedElement(instance, currentElement, props); - } - } - } - - propSetter.activate(); - return layout; - } - catch (Exception oops) { - if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("Could not create the Layout. Reported error follows.", - oops); - return null; - } - } - - protected - void parseRenderer(Element element) { - String renderingClass = subst(element.getAttribute(RENDERING_CLASS_ATTR)); - String renderedClass = subst(element.getAttribute(RENDERED_CLASS_ATTR)); - if(repository instanceof RendererSupport) { - RendererMap.addRenderer((RendererSupport) repository, renderedClass, - renderingClass); - } - } - - /** - * Parses throwable renderer. - * @param element throwableRenderer element. - * @return configured throwable renderer. - * @since 1.2.16. - */ - protected ThrowableRenderer parseThrowableRenderer(final Element element) { - String className = subst(element.getAttribute(CLASS_ATTR)); - LogLog.debug("Parsing throwableRenderer of class: \""+className+"\""); - try { - Object instance = Loader.loadClass(className).newInstance(); - ThrowableRenderer tr = (ThrowableRenderer)instance; - PropertySetter propSetter = new PropertySetter(tr); - - NodeList params = element.getChildNodes(); - final int length = params.getLength(); - - for (int loop = 0; loop < length; loop++) { - Node currentNode = (Node)params.item(loop); - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - Element currentElement = (Element) currentNode; - String tagName = currentElement.getTagName(); - if(tagName.equals(PARAM_TAG)) { - setParameter(currentElement, propSetter); - } else { - parseUnrecognizedElement(instance, currentElement, props); - } - } - } - - propSetter.activate(); - return tr; - } - catch (Exception oops) { - if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("Could not create the ThrowableRenderer. Reported error follows.", - oops); - return null; - } - } - - /** - Used internally to parse a level element. - */ - protected - void parseLevel(Element element, Logger logger, boolean isRoot) { - String catName = logger.getName(); - if(isRoot) { - catName = "root"; - } - - String priStr = subst(element.getAttribute(VALUE_ATTR)); - LogLog.debug("Level value for "+catName+" is ["+priStr+"]."); - - if(INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) { - if(isRoot) { - LogLog.error("Root level cannot be inherited. Ignoring directive."); - } else { - logger.setLevel(null); - } - } else { - String className = subst(element.getAttribute(CLASS_ATTR)); - if(EMPTY_STR.equals(className)) { - logger.setLevel(OptionConverter.toLevel(priStr, Level.DEBUG)); - } else { - LogLog.debug("Desired Level sub-class: ["+className+']'); - try { - Class clazz = Loader.loadClass(className); - Method toLevelMethod = clazz.getMethod("toLevel", - ONE_STRING_PARAM); - Level pri = (Level) toLevelMethod.invoke(null, - new Object[] {priStr}); - logger.setLevel(pri); - } catch (Exception oops) { - if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("Could not create level ["+priStr+ - "]. Reported error follows.", oops); - return; - } - } - } - LogLog.debug(catName + " level set to " + logger.getLevel()); - } - - protected - void setParameter(Element elem, PropertySetter propSetter) { - String name = subst(elem.getAttribute(NAME_ATTR)); - String value = (elem.getAttribute(VALUE_ATTR)); - value = subst(OptionConverter.convertSpecialChars(value)); - propSetter.setProperty(name, value); - } - - - /** - Configure log4j using a configuration element as - defined in the log4j.dtd. - - */ - static - public - void configure (Element element) { - DOMConfigurator configurator = new DOMConfigurator(); - configurator.doConfigure(element, LogManager.getLoggerRepository()); - } - - /** - Like {@link #configureAndWatch(String, long)} except that the - default delay as defined by {@link FileWatchdog#DEFAULT_DELAY} is - used. - - @param configFilename A log4j configuration file in XML format. - - */ - static - public - void configureAndWatch(String configFilename) { - configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY); - } - - /** - Read the configuration file configFilename if it - exists. Moreover, a thread will be created that will periodically - check if configFilename has been created or - modified. The period is determined by the delay - argument. If a change or file creation is detected, then - configFilename is read to configure log4j. - - @param configFilename A log4j configuration file in XML format. - @param delay The delay in milliseconds to wait between each check. - */ - static - public - void configureAndWatch(String configFilename, long delay) { - XMLWatchdog xdog = new XMLWatchdog(configFilename); - xdog.setDelay(delay); - xdog.start(); - } - - private interface ParseAction { - Document parse(final DocumentBuilder parser) throws SAXException, IOException; - } - - - public - void doConfigure(final String filename, LoggerRepository repository) { - ParseAction action = new ParseAction() { - public Document parse(final DocumentBuilder parser) throws SAXException, IOException { - return parser.parse(new File(filename)); - } - public String toString() { - return "file [" + filename + "]"; - } - }; - doConfigure(action, repository); - } - - - public - void doConfigure(final URL url, LoggerRepository repository) { - ParseAction action = new ParseAction() { - public Document parse(final DocumentBuilder parser) throws SAXException, IOException { - URLConnection uConn = url.openConnection(); - uConn.setUseCaches(false); - InputSource src = new InputSource(uConn.getInputStream()); - src.setSystemId(url.toString()); - return parser.parse(src); - } - public String toString() { - return "url [" + url.toString() + "]"; - } - }; - doConfigure(action, repository); - } - - /** - Configure log4j by reading in a log4j.dtd compliant XML - configuration file. - - */ - public - void doConfigure(final InputStream inputStream, LoggerRepository repository) - throws FactoryConfigurationError { - ParseAction action = new ParseAction() { - public Document parse(final DocumentBuilder parser) throws SAXException, IOException { - InputSource inputSource = new InputSource(inputStream); - inputSource.setSystemId("dummy://log4j.dtd"); - return parser.parse(inputSource); - } - public String toString() { - return "input stream [" + inputStream.toString() + "]"; - } - }; - doConfigure(action, repository); - } - - /** - Configure log4j by reading in a log4j.dtd compliant XML - configuration file. - - */ - public - void doConfigure(final Reader reader, LoggerRepository repository) - throws FactoryConfigurationError { - ParseAction action = new ParseAction() { - public Document parse(final DocumentBuilder parser) throws SAXException, IOException { - InputSource inputSource = new InputSource(reader); - inputSource.setSystemId("dummy://log4j.dtd"); - return parser.parse(inputSource); - } - public String toString() { - return "reader [" + reader.toString() + "]"; - } - }; - doConfigure(action, repository); - } - - /** - Configure log4j by reading in a log4j.dtd compliant XML - configuration file. - - */ - protected - void doConfigure(final InputSource inputSource, LoggerRepository repository) - throws FactoryConfigurationError { - if (inputSource.getSystemId() == null) { - inputSource.setSystemId("dummy://log4j.dtd"); - } - ParseAction action = new ParseAction() { - public Document parse(final DocumentBuilder parser) throws SAXException, IOException { - return parser.parse(inputSource); - } - public String toString() { - return "input source [" + inputSource.toString() + "]"; - } - }; - doConfigure(action, repository); - } - - - private final void doConfigure(final ParseAction action, final LoggerRepository repository) - throws FactoryConfigurationError { - DocumentBuilderFactory dbf = null; - this.repository = repository; - try { - LogLog.debug("System property is :"+ - OptionConverter.getSystemProperty(dbfKey, - null)); - dbf = DocumentBuilderFactory.newInstance(); - LogLog.debug("Standard DocumentBuilderFactory search succeded."); - LogLog.debug("DocumentBuilderFactory is: "+dbf.getClass().getName()); - } catch(FactoryConfigurationError fce) { - Exception e = fce.getException(); - LogLog.debug("Could not instantiate a DocumentBuilderFactory.", e); - throw fce; - } - - try { - dbf.setValidating(true); - - DocumentBuilder docBuilder = dbf.newDocumentBuilder(); - - docBuilder.setErrorHandler(new SAXErrorHandler()); - docBuilder.setEntityResolver(new Log4jEntityResolver()); - - Document doc = action.parse(docBuilder); - parse(doc.getDocumentElement()); - } catch (Exception e) { - if (e instanceof InterruptedException || e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - // I know this is miserable... - LogLog.error("Could not parse "+ action.toString() + ".", e); - } - } - - /** - Configure by taking in an DOM element. - */ - public void doConfigure(Element element, LoggerRepository repository) { - this.repository = repository; - parse(element); - } - - - /** - A static version of {@link #doConfigure(String, LoggerRepository)}. */ - static - public - void configure(String filename) throws FactoryConfigurationError { - new DOMConfigurator().doConfigure(filename, - LogManager.getLoggerRepository()); - } - - /** - A static version of {@link #doConfigure(URL, LoggerRepository)}. - */ - static - public - void configure(URL url) throws FactoryConfigurationError { - new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository()); - } - - /** - Used internally to configure the log4j framework by parsing a DOM - tree of XML elements based on log4j.dtd. - - */ - protected - void parse(Element element) { - - String rootElementName = element.getTagName(); - - if (!rootElementName.equals(CONFIGURATION_TAG)) { - if(rootElementName.equals(OLD_CONFIGURATION_TAG)) { - LogLog.warn("The <"+OLD_CONFIGURATION_TAG+ - "> element has been deprecated."); - LogLog.warn("Use the <"+CONFIGURATION_TAG+"> element instead."); - } else { - LogLog.error("DOM element is - not a <"+CONFIGURATION_TAG+"> element."); - return; - } - } - - - String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR)); - - LogLog.debug("debug attribute= \"" + debugAttrib +"\"."); - // if the log4j.dtd is not specified in the XML file, then the - // "debug" attribute is returned as the empty string. - if(!debugAttrib.equals("") && !debugAttrib.equals("null")) { - LogLog.setInternalDebugging(OptionConverter.toBoolean(debugAttrib, true)); - } else { - LogLog.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute."); - } - - // - // reset repository before configuration if reset="true" - // on configuration element. - // - String resetAttrib = subst(element.getAttribute(RESET_ATTR)); - LogLog.debug("reset attribute= \"" + resetAttrib +"\"."); - if(!("".equals(resetAttrib))) { - if (OptionConverter.toBoolean(resetAttrib, false)) { - repository.resetConfiguration(); - } - } - - - - String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR)); - if(!confDebug.equals("") && !confDebug.equals("null")) { - LogLog.warn("The \""+CONFIG_DEBUG_ATTR+"\" attribute is deprecated."); - LogLog.warn("Use the \""+INTERNAL_DEBUG_ATTR+"\" attribute instead."); - LogLog.setInternalDebugging(OptionConverter.toBoolean(confDebug, true)); - } - - String thresholdStr = subst(element.getAttribute(THRESHOLD_ATTR)); - LogLog.debug("Threshold =\"" + thresholdStr +"\"."); - if(!"".equals(thresholdStr) && !"null".equals(thresholdStr)) { - repository.setThreshold(thresholdStr); - } - - //Hashtable appenderBag = new Hashtable(11); - - /* Building Appender objects, placing them in a local namespace - for future reference */ - - // First configure each category factory under the root element. - // Category factories need to be configured before any of - // categories they support. - // - String tagName = null; - Element currentElement = null; - Node currentNode = null; - NodeList children = element.getChildNodes(); - final int length = children.getLength(); - - for (int loop = 0; loop < length; loop++) { - currentNode = children.item(loop); - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - currentElement = (Element) currentNode; - tagName = currentElement.getTagName(); - - if (tagName.equals(CATEGORY_FACTORY_TAG) || tagName.equals(LOGGER_FACTORY_TAG)) { - parseCategoryFactory(currentElement); - } - } - } - - for (int loop = 0; loop < length; loop++) { - currentNode = children.item(loop); - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - currentElement = (Element) currentNode; - tagName = currentElement.getTagName(); - - if (tagName.equals(CATEGORY) || tagName.equals(LOGGER)) { - parseCategory(currentElement); - } else if (tagName.equals(ROOT_TAG)) { - parseRoot(currentElement); - } else if(tagName.equals(RENDERER_TAG)) { - parseRenderer(currentElement); - } else if(tagName.equals(THROWABLE_RENDERER_TAG)) { - if (repository instanceof ThrowableRendererSupport) { - ThrowableRenderer tr = parseThrowableRenderer(currentElement); - if (tr != null) { - ((ThrowableRendererSupport) repository).setThrowableRenderer(tr); - } - } - } else if (!(tagName.equals(APPENDER_TAG) - || tagName.equals(CATEGORY_FACTORY_TAG) - || tagName.equals(LOGGER_FACTORY_TAG))) { - quietParseUnrecognizedElement(repository, currentElement, props); - } - } - } - } - - - protected - String subst(final String value) { - return subst(value, props); - } - - /** - * Substitutes property value for any references in expression. - * - * @param value value from configuration file, may contain - * literal text, property references or both - * @param props properties. - * @return evaluated expression, may still contain expressions - * if unable to expand. - * @since 1.2.15 - */ - public static String subst(final String value, final Properties props) { - try { - return OptionConverter.substVars(value, props); - } catch (IllegalArgumentException e) { - LogLog.warn("Could not perform variable substitution.", e); - return value; - } - } - - - /** - * Sets a parameter based from configuration file content. - * - * @param elem param element, may not be null. - * @param propSetter property setter, may not be null. - * @param props properties - * @since 1.2.15 - */ - public static void setParameter(final Element elem, - final PropertySetter propSetter, - final Properties props) { - String name = subst(elem.getAttribute("name"), props); - String value = (elem.getAttribute("value")); - value = subst(OptionConverter.convertSpecialChars(value), props); - propSetter.setProperty(name, value); - } - - /** - * Creates an object and processes any nested param elements - * but does not call activateOptions. If the class also supports - * UnrecognizedElementParser, the parseUnrecognizedElement method - * will be call for any child elements other than param. - * - * @param element element, may not be null. - * @param props properties - * @param expectedClass interface or class expected to be implemented - * by created class - * @return created class or null. - * @throws Exception thrown if the contain object should be abandoned. - * @since 1.2.15 - */ - public static Object parseElement(final Element element, - final Properties props, - final Class expectedClass) throws Exception { - String clazz = subst(element.getAttribute("class"), props); - Object instance = OptionConverter.instantiateByClassName(clazz, - expectedClass, null); - - if (instance != null) { - PropertySetter propSetter = new PropertySetter(instance); - NodeList children = element.getChildNodes(); - final int length = children.getLength(); - - for (int loop = 0; loop < length; loop++) { - Node currentNode = children.item(loop); - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - Element currentElement = (Element) currentNode; - String tagName = currentElement.getTagName(); - if (tagName.equals("param")) { - setParameter(currentElement, propSetter, props); - } else { - parseUnrecognizedElement(instance, currentElement, props); - } - } - } - return instance; - } - return null; - } - -} - - -class XMLWatchdog extends FileWatchdog { - - XMLWatchdog(String filename) { - super(filename); - } - - /** - Call {@link DOMConfigurator#configure(String)} with the - filename to reconfigure log4j. */ - public - void doOnChange() { - new DOMConfigurator().doConfigure(filename, - LogManager.getLoggerRepository()); - } -} diff --git a/java/src/org/apache/log4j/xml/Log4jEntityResolver.java b/java/src/org/apache/log4j/xml/Log4jEntityResolver.java deleted file mode 100644 index 94125a6..0000000 --- a/java/src/org/apache/log4j/xml/Log4jEntityResolver.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.xml; - -import org.apache.log4j.helpers.LogLog; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; - -import java.io.InputStream; -import java.io.ByteArrayInputStream; - -/** - * An {@link EntityResolver} specifically designed to return - * log4j.dtd which is embedded within the log4j jar - * file. - * - * @author Paul Austin - * */ -public class Log4jEntityResolver implements EntityResolver { - private static final String PUBLIC_ID = "-//APACHE//DTD LOG4J 1.2//EN"; - - public InputSource resolveEntity (String publicId, String systemId) { - if (systemId.endsWith("log4j.dtd") || PUBLIC_ID.equals(publicId)) { - Class clazz = getClass(); - InputStream in = clazz.getResourceAsStream("/org/apache/log4j/xml/log4j.dtd"); - if (in == null) { - LogLog.warn("Could not find [log4j.dtd] using [" + clazz.getClassLoader() - + "] class loader, parsed without DTD."); - in = new ByteArrayInputStream(new byte[0]); - } - return new InputSource(in); - } else { - return null; - } - } -} diff --git a/java/src/org/apache/log4j/xml/SAXErrorHandler.java b/java/src/org/apache/log4j/xml/SAXErrorHandler.java deleted file mode 100644 index 43e851b..0000000 --- a/java/src/org/apache/log4j/xml/SAXErrorHandler.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.log4j.xml; - -import org.xml.sax.ErrorHandler; -import org.xml.sax.SAXParseException; -import org.apache.log4j.helpers.LogLog; - -public class SAXErrorHandler implements ErrorHandler { - - public - void error(final SAXParseException ex) { - emitMessage("Continuable parsing error ", ex); - } - - public - void fatalError(final SAXParseException ex) { - emitMessage("Fatal parsing error ", ex); - } - - public - void warning(final SAXParseException ex) { - emitMessage("Parsing warning ", ex); - } - - private static void emitMessage(final String msg, final SAXParseException ex) { - LogLog.warn(msg +ex.getLineNumber()+" and column " - +ex.getColumnNumber()); - LogLog.warn(ex.getMessage(), ex.getException()); - } -} diff --git a/java/src/org/apache/log4j/xml/UnrecognizedElementHandler.java b/java/src/org/apache/log4j/xml/UnrecognizedElementHandler.java deleted file mode 100644 index 463d5d9..0000000 --- a/java/src/org/apache/log4j/xml/UnrecognizedElementHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.log4j.xml; - -import org.w3c.dom.Element; -import java.util.Properties; - -/** - * When implemented by an object configured by DOMConfigurator, - * the handle method will be called when an unrecognized child - * element is encountered. Unrecognized child elements of - * the log4j:configuration element will be dispatched to - * the logger repository if it supports this interface. - * - * @since 1.2.15 - */ -public interface UnrecognizedElementHandler { - /** - * Called to inform a configured object when - * an unrecognized child element is encountered. - * @param element element, may not be null. - * @param props properties in force, may be null. - * @return true if configured object recognized the element - * @throws Exception throw an exception to prevent activation - * of the configured object. - */ - boolean parseUnrecognizedElement(Element element, Properties props) throws Exception; -} \ No newline at end of file diff --git a/java/src/org/apache/log4j/xml/XMLLayout.java b/java/src/org/apache/log4j/xml/XMLLayout.java deleted file mode 100644 index 2062a80..0000000 --- a/java/src/org/apache/log4j/xml/XMLLayout.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Contributors: Mathias Bogaert - -package org.apache.log4j.xml; - -import org.apache.log4j.Layout; -import org.apache.log4j.helpers.Transform; -import org.apache.log4j.spi.LocationInfo; -import org.apache.log4j.spi.LoggingEvent; - -import java.util.Set; -import java.util.Arrays; - -/** - * The output of the XMLLayout consists of a series of log4j:event - * elements as defined in the log4j.dtd. It does not output a - * complete well-formed XML file. The output is designed to be - * included as an external entity in a separate file to form - * a correct XML file. - * - *

For example, if abc is the name of the file where - * the XMLLayout ouput goes, then a well-formed XML file would be: - * -

-   <?xml version="1.0" ?>
- 
-  <!DOCTYPE log4j:eventSet PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd" [<!ENTITY data SYSTEM "abc">]>
- 
-  <log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/">
- 	  &data;
-  </log4j:eventSet>
-  
- - *

This approach enforces the independence of the XMLLayout and the - * appender where it is embedded. - * - *

The version attribute helps components to correctly - * intrepret output generated by XMLLayout. The value of this - * attribute should be "1.1" for output generated by log4j versions - * prior to log4j 1.2 (final release) and "1.2" for relase 1.2 and - * later. - * - * Appenders using this layout should have their encoding - * set to UTF-8 or UTF-16, otherwise events containing - * non ASCII characters could result in corrupted - * log files. - * - * @author Ceki Gülcü - * @since 0.9.0 - * */ -public class XMLLayout extends Layout { - - private final int DEFAULT_SIZE = 256; - private final int UPPER_LIMIT = 2048; - - private StringBuffer buf = new StringBuffer(DEFAULT_SIZE); - private boolean locationInfo = false; - private boolean properties = false; - - /** - * The LocationInfo option takes a boolean value. By default, - * it is set to false which means there will be no location - * information output by this layout. If the the option is set to - * true, then the file name and line number of the statement at the - * origin of the log statement will be output. - * - *

If you are embedding this layout within an {@link - * org.apache.log4j.net.SMTPAppender} then make sure to set the - * LocationInfo option of that appender as well. - * */ - public void setLocationInfo(boolean flag) { - locationInfo = flag; - } - - /** - Returns the current value of the LocationInfo option. - */ - public boolean getLocationInfo() { - return locationInfo; - } - - /** - * Sets whether MDC key-value pairs should be output, default false. - * @param flag new value. - * @since 1.2.15 - */ - public void setProperties(final boolean flag) { - properties = flag; - } - - /** - * Gets whether MDC key-value pairs should be output. - * @return true if MDC key-value pairs are output. - * @since 1.2.15 - */ - public boolean getProperties() { - return properties; - } - - /** No options to activate. */ - public void activateOptions() { - } - - - /** - * Formats a {@link org.apache.log4j.spi.LoggingEvent} in conformance with the log4j.dtd. - * */ - public String format(final LoggingEvent event) { - - // Reset working buffer. If the buffer is too large, then we need a new - // one in order to avoid the penalty of creating a large array. - if(buf.capacity() > UPPER_LIMIT) { - buf = new StringBuffer(DEFAULT_SIZE); - } else { - buf.setLength(0); - } - - // We yield to the \r\n heresy. - - buf.append("\r\n"); - - buf.append("\r\n"); - - String ndc = event.getNDC(); - if(ndc != null) { - buf.append("\r\n"); - } - - String[] s = event.getThrowableStrRep(); - if(s != null) { - buf.append("\r\n"); - } - - if(locationInfo) { - LocationInfo locationInfo = event.getLocationInformation(); - buf.append("\r\n"); - } - - if (properties) { - Set keySet = event.getPropertyKeySet(); - if (keySet.size() > 0) { - buf.append("\r\n"); - Object[] keys = keySet.toArray(); - Arrays.sort(keys); - for (int i = 0; i < keys.length; i++) { - String key = keys[i].toString(); - Object val = event.getMDC(key); - if (val != null) { - buf.append("\r\n"); - } - } - buf.append("\r\n"); - } - } - - buf.append("\r\n\r\n"); - - return buf.toString(); - } - - /** - The XMLLayout prints and does not ignore exceptions. Hence the - return value false. - */ - public boolean ignoresThrowable() { - return false; - } -} diff --git a/java/src/org/apache/log4j/xml/package.html b/java/src/org/apache/log4j/xml/package.html deleted file mode 100644 index 8a9ed6e..0000000 --- a/java/src/org/apache/log4j/xml/package.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - -

XML based components. - - -


-
- -Last modified: Mon Mar 27 21:17:13 MDT 2000 - -

See the short manual for an - * introduction on this class. - *