├── .gitignore ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main └── java │ └── org │ └── xbib │ └── logging │ └── log4j2 │ ├── ElasticsearchConnection.java │ ├── ElasticsearchObject.java │ ├── ElasticsearchProvider.java │ └── ElasticsearchTransportClient.java ├── site └── site.xml └── test ├── java ├── ElasticsearchAppenderTest.java └── org │ └── apache │ └── logging │ └── log4j │ └── nosql │ └── appender │ ├── NoSqlAppenderTest.java │ └── NoSqlDatabaseManagerTest.java └── resources └── log4j2.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /data 2 | /work 3 | /logs 4 | /.idea 5 | /target 6 | .DS_Store 7 | *.iml 8 | /.settings 9 | /.classpath 10 | /.project 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | :warning: **This repository will be removed soon without further notice** 2 | 3 | # Log4j2 Elasticsearch appender 4 | 5 | This NoSql Log4j2 appender logs messages to Elasticsearch. 6 | 7 | It uses an Elasticsearch TransportClient instance with bulk indexing strategy to connect to a remote server 8 | that is a node of a running Elasticsearch cluster. 9 | 10 | In the `log4j2.xml` configuration file, you can specify the following parameters: 11 | 12 | `cluster` the Elasticsearch cluster name (default: `elasticsearch`) 13 | 14 | `host` the Elasticsearch node in the cluste to connect to (default: `localhost`) 15 | 16 | `port` the port of the Elasticsearch node where the transport protocol is available (default: 9300) 17 | 18 | `index` the index name of the Elasticsearch cluster to write log messages to (default: `log4j2`). 19 | The index name may be a date format string like 'log4j2-'yyyyMMdd 20 | 21 | `type` the type of the Elasticsearch index to write log messages to (default: `log4j2`) 22 | 23 | For advanced usage, there are the following settings: 24 | 25 | `sniff` true for all Elasticsearch nodes should be used to connect to, false if only one node should be used (default: `false`) 26 | 27 | `timeout` timeout for Elasticsearch cluster ping (default: `30s`) 28 | 29 | `maxActionsPerBulkRequest` maximum number of indexing action in a single bulk request (default: `1000`) 30 | 31 | `maxConcurrentBulkRequests` maxmimum number of concurrent bulk request (default: 2* number of available CPUs) 32 | 33 | `maxVolumePerBulkRequest` maximum volume of a single bulk request (default: `10mb`) 34 | 35 | `flushInterval` maximum wait time before a single bulk request is flushed to the Elasticsearch cluster (default: `30s`) 36 | 37 | ## Log4j2.xml example 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ## Java code example 62 | 63 | Logger logger = LogManager.getLogger("test"); 64 | logger.info("Hello World"); 65 | 66 | ## Indexed log message example 67 | 68 | curl 'localhost:9200/log4j2/_search?pretty' 69 | { 70 | "took" : 1, 71 | "timed_out" : false, 72 | "_shards" : { 73 | "total" : 5, 74 | "successful" : 5, 75 | "failed" : 0 76 | }, 77 | "hits" : { 78 | "total" : 1, 79 | "max_score" : 1.0, 80 | "hits" : [ { 81 | "_index" : "log4j2", 82 | "_type" : "log4j2", 83 | "_id" : "dzvP2kbtS8Sr0uEZojMfKg", 84 | "_score" : 1.0, 85 | "_source":{"date":"2014-07-18T06:17:38.896Z","contextStack":[],"level":"INFO", 86 | "marker":null,"thrown":null,"source":{"fileName":"ElasticsearchAppenderTest.java", 87 | "methodName":"testLog","className":"ElasticsearchAppenderTest","lineNumber":11}, 88 | "loggerName":"test","message":"Hello World","millis":1405664258896, 89 | "contextMap":{},"threadName":"main"} 90 | } ] 91 | } 92 | } 93 | 94 | 95 | # Versions 96 | 97 | | Log4j2 Elasticsearch appender | Elasticssearch Version | Release date | 98 | | --------------------------------| -----------------------|--------------| 99 | | 1.7.2.0 | 1.7.2 | Sep 16, 2015 | 100 | | 1.4.0.1 | 1.4.0 | Nov 21, 2014 | 101 | | 1.4.0.0 | 1.4.0 | Nov 12, 2014 | 102 | | 1.0.0 | 1.2.2 | Jul 18, 2014 | 103 | 104 | 105 | # Installation 106 | 107 | Maven coordinates 108 | 109 | 110 | 111 | xbib 112 | http://xbib.org/repository 113 | 114 | 115 | 116 | 117 | 118 | org.xbib.logging.log4j2 119 | log4j2-elasticsearch 120 | 1.7.2.0 121 | 122 | 123 | 124 | 125 | # Project docs 126 | 127 | The Maven project site is available at [Github](http://jprante.github.io/log4j2-elasticsearch) 128 | 129 | # License 130 | 131 | Log4j2 Elasticsearch Appender 132 | 133 | Copyright (C) 2014 Jörg Prante 134 | 135 | Licensed under the Apache License, Version 2.0 (the "License"); 136 | you may not use this file except in compliance with the License. 137 | You may obtain a copy of the License at 138 | 139 | http://www.apache.org/licenses/LICENSE-2.0 140 | 141 | Unless required by applicable law or agreed to in writing, software 142 | distributed under the License is distributed on an "AS IS" BASIS, 143 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 144 | See the License for the specific language governing permissions and 145 | limitations under the License. 146 | 147 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | 8 | log4j2-elasticsearch 9 | Log4j2 Appender for Elasticsearch 10 | 11 | org.xbib.logging.log4j2 12 | log4j2-elasticsearch 13 | 1.7.2.0 14 | jar 15 | 16 | 2014 17 | 18 | 19 | 20 | Apache License 21 | http://www.apache.org/licenses/LICENSE-2.0 22 | repo 23 | 24 | 25 | 26 | 27 | scm:git:git@github.com:jprante/log4j2-elasticsearch.git 28 | scm:git:git@github.com:jprante/log4j2-elasticsearch.git 29 | http://github.com/jprante/elasticsearch-xml 30 | 31 | 32 | 33 | 34 | xbib.org 35 | scpexe://xbib.org/repository 36 | 37 | 38 | 39 | 40 | github 41 | UTF-8 42 | 1.7 43 | 2.2 44 | 1.7.2 45 | 46 | 47 | 48 | 49 | 50 | org.apache.logging.log4j 51 | log4j-nosql 52 | ${log4j2.version} 53 | jar 54 | compile 55 | 56 | 57 | 58 | org.elasticsearch 59 | elasticsearch 60 | ${elasticsearch.version} 61 | jar 62 | compile 63 | 64 | 65 | 66 | junit 67 | junit 68 | 4.12 69 | test 70 | 71 | 72 | 73 | org.easymock 74 | easymock 75 | 3.2 76 | test 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | org.apache.maven.wagon 85 | wagon-ssh-external 86 | 2.9 87 | 88 | 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-compiler-plugin 93 | 3.3 94 | 95 | ${java.compile.version} 96 | ${java.compile.version} 97 | UTF-8 98 | true 99 | true 100 | true 101 | -Xlint:all,-serial,-path,-rawtypes,-unchecked 102 | 103 | 104 | 105 | org.apache.maven.plugins 106 | maven-surefire-plugin 107 | 2.18.1 108 | 109 | false 110 | 111 | **/*Test.java 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-source-plugin 118 | 2.4 119 | 120 | 121 | attach-sources 122 | 123 | jar 124 | 125 | 126 | 127 | 128 | 129 | maven-resources-plugin 130 | 2.6 131 | 132 | UTF-8 133 | 134 | 135 | 136 | maven-javadoc-plugin 137 | 2.10.3 138 | 139 | ${project.build.sourceEncoding} 140 | en 141 | true 142 | true 143 | 144 | 145 | 146 | package 147 | 148 | jar 149 | 150 | 151 | 152 | 153 | 154 | maven-site-plugin 155 | 3.3 156 | 157 | en 158 | UTF-8 159 | UTF-8 160 | 161 | 162 | 163 | com.github.github 164 | site-maven-plugin 165 | 0.12 166 | 167 | Building site for ${project.version} 168 | 169 | 170 | 171 | 172 | site 173 | 174 | site 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | maven-javadoc-plugin 185 | 2.10.3 186 | 187 | ${project.build.sourceEncoding} 188 | en 189 | true 190 | true 191 | 192 | 193 | 194 | maven-surefire-report-plugin 195 | 2.18.1 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /src/main/java/org/xbib/logging/log4j2/ElasticsearchConnection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Jörg Prante 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.xbib.logging.log4j2; 17 | 18 | import org.apache.logging.log4j.core.appender.AppenderLoggingException; 19 | import org.apache.logging.log4j.nosql.appender.NoSqlConnection; 20 | import org.apache.logging.log4j.nosql.appender.NoSqlObject; 21 | 22 | import java.util.Map; 23 | import java.util.concurrent.atomic.AtomicBoolean; 24 | 25 | public class ElasticsearchConnection implements NoSqlConnection, ElasticsearchObject> { 26 | 27 | private final ElasticsearchTransportClient client; 28 | 29 | private final AtomicBoolean closed = new AtomicBoolean(false); 30 | 31 | public ElasticsearchConnection(final ElasticsearchTransportClient client) { 32 | this.client = client; 33 | } 34 | 35 | @Override 36 | public ElasticsearchObject createObject() { 37 | return new ElasticsearchObject(); 38 | } 39 | 40 | @Override 41 | public ElasticsearchObject[] createList(final int length) { 42 | return new ElasticsearchObject[length]; 43 | } 44 | 45 | @Override 46 | public void insertObject(final NoSqlObject> object) { 47 | try { 48 | client.index(object.unwrap()); 49 | } catch (Exception e) { 50 | throw new AppenderLoggingException("failed to write log event to Elasticsearch: " + e.getMessage(), e); 51 | } 52 | } 53 | 54 | @Override 55 | public void close() { 56 | if (closed.compareAndSet(false, true)) { 57 | client.close(); 58 | } 59 | } 60 | 61 | @Override 62 | public boolean isClosed() { 63 | return closed.get(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/xbib/logging/log4j2/ElasticsearchObject.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Jörg Prante 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.xbib.logging.log4j2; 17 | 18 | import org.apache.logging.log4j.nosql.appender.DefaultNoSqlObject; 19 | 20 | public class ElasticsearchObject extends DefaultNoSqlObject { 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/xbib/logging/log4j2/ElasticsearchProvider.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Jörg Prante 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.xbib.logging.log4j2; 17 | 18 | import org.apache.logging.log4j.Logger; 19 | import org.apache.logging.log4j.core.config.plugins.Plugin; 20 | import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 21 | import org.apache.logging.log4j.core.config.plugins.PluginFactory; 22 | import org.apache.logging.log4j.nosql.appender.NoSqlProvider; 23 | import org.apache.logging.log4j.status.StatusLogger; 24 | import org.elasticsearch.client.transport.TransportClient; 25 | import org.elasticsearch.common.settings.Settings; 26 | import org.elasticsearch.common.transport.InetSocketTransportAddress; 27 | import org.elasticsearch.common.unit.ByteSizeValue; 28 | import org.elasticsearch.common.unit.TimeValue; 29 | 30 | import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; 31 | 32 | @Plugin(name = "Elasticsearch", category = "Core", printObject = true) 33 | public class ElasticsearchProvider implements NoSqlProvider { 34 | 35 | private static final Logger logger = StatusLogger.getLogger(); 36 | 37 | private final ElasticsearchTransportClient client; 38 | 39 | private final String description; 40 | 41 | private ElasticsearchProvider(final ElasticsearchTransportClient client, final String description) { 42 | this.client = client; 43 | this.description = "elasticsearch{ " + description + " }"; 44 | } 45 | 46 | @Override 47 | public ElasticsearchConnection getConnection() { 48 | //TODO validate client is viable 49 | return new ElasticsearchConnection(client); 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return description; 55 | } 56 | 57 | /** 58 | * Factory method for creating an Elasticsearch provider within the plugin manager. 59 | * 60 | * @param cluster The name of the Elasticsearch cluster to which log event documents will be written. 61 | * @param host The host name an Elasticsearch server node of the cluster, defaults to localhost. 62 | * @param port The port that Elasticsearch is listening on, defaults to 9300 63 | * @param index The index that Elasticsearch shall use for indexing 64 | * @param type The type of the index Elasticsearch shall use for indexing 65 | * @return a new Elasticsearch provider 66 | */ 67 | @PluginFactory 68 | public static ElasticsearchProvider createNoSqlProvider( 69 | @PluginAttribute("cluster") String cluster, 70 | @PluginAttribute("host") String host, 71 | @PluginAttribute("port") Integer port, 72 | @PluginAttribute("index") String index, 73 | @PluginAttribute("type") String type, 74 | @PluginAttribute("timeout") String timeout, 75 | @PluginAttribute("maxActionsPerBulkRequest") Integer maxActionsPerBulkRequest, 76 | @PluginAttribute("maxConcurrentBulkRequests") Integer maxConcurrentBulkRequests, 77 | @PluginAttribute("maxVolumePerBulkRequest") String maxVolumePerBulkRequest, 78 | @PluginAttribute("flushInterval") String flushInterval) { 79 | 80 | if (cluster == null || cluster.isEmpty()) { 81 | cluster = "elasticsearch"; 82 | } 83 | if (host == null || host.isEmpty()) { 84 | host = "localhost"; 85 | } 86 | if (port == null || port == 0) { 87 | port = 9300; 88 | } 89 | if (index == null || index.isEmpty()) { 90 | index = "log4j2"; 91 | } 92 | if (type == null || type.isEmpty()) { 93 | type = "log4j2"; 94 | } 95 | if (timeout == null || timeout.isEmpty()) { 96 | timeout = "30s"; 97 | } 98 | if (maxActionsPerBulkRequest == null) { 99 | maxActionsPerBulkRequest = 1000; 100 | } 101 | if (maxConcurrentBulkRequests == null) { 102 | maxConcurrentBulkRequests = 2 * Runtime.getRuntime().availableProcessors(); 103 | } 104 | if (maxVolumePerBulkRequest == null || maxVolumePerBulkRequest.isEmpty()) { 105 | maxVolumePerBulkRequest = "10m"; 106 | } 107 | 108 | Settings settings = settingsBuilder() 109 | .put("cluster.name", cluster) 110 | .put("network.server", false) 111 | .put("node.client", true) 112 | .put("client.transport.sniff", true) 113 | .put("client.transport.ping_timeout", timeout) 114 | .put("client.transport.ignore_cluster_name", false) 115 | .put("client.transport.nodes_sampler_interval", "30s") 116 | .build(); 117 | 118 | TransportClient client = new TransportClient(settings, false); 119 | client.addTransportAddress(new InetSocketTransportAddress(host, port)); 120 | if (client.connectedNodes().isEmpty()) { 121 | logger.error("unable to connect to Elasticsearch cluster"); 122 | return null; 123 | } 124 | String description = "cluster=" + cluster + ",host=" + host + ",port=" + port + ",index=" + index + ",type=" + type; 125 | ElasticsearchTransportClient elasticsearchTransportClient = new ElasticsearchTransportClient(client, index, type, 126 | maxActionsPerBulkRequest, maxConcurrentBulkRequests, 127 | ByteSizeValue.parseBytesSizeValue(maxVolumePerBulkRequest), 128 | TimeValue.parseTimeValue(flushInterval, TimeValue.timeValueSeconds(30))); 129 | ElasticsearchProvider elasticsearchProvider = new ElasticsearchProvider(elasticsearchTransportClient, description); 130 | 131 | return elasticsearchProvider; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/xbib/logging/log4j2/ElasticsearchTransportClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Jörg Prante 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.xbib.logging.log4j2; 17 | 18 | import org.elasticsearch.ElasticsearchIllegalStateException; 19 | import org.elasticsearch.action.bulk.BulkProcessor; 20 | import org.elasticsearch.action.bulk.BulkRequest; 21 | import org.elasticsearch.action.bulk.BulkResponse; 22 | import org.elasticsearch.action.index.IndexRequest; 23 | import org.elasticsearch.client.Client; 24 | import org.elasticsearch.client.transport.TransportClient; 25 | import org.elasticsearch.common.unit.ByteSizeValue; 26 | import org.elasticsearch.common.unit.TimeValue; 27 | 28 | import java.text.SimpleDateFormat; 29 | import java.util.Date; 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | 33 | public class ElasticsearchTransportClient { 34 | 35 | private final Client client; 36 | 37 | private final BulkProcessor bulkProcessor; 38 | 39 | private final String index; 40 | 41 | private final String type; 42 | 43 | public ElasticsearchTransportClient(Client client, String index, String type, 44 | int maxActionsPerBulkRequest, 45 | int maxConcurrentBulkRequests, 46 | ByteSizeValue maxVolumePerBulkRequest, 47 | TimeValue flushInterval) { 48 | this.client = client; 49 | this.index = index; 50 | this.type = type; 51 | BulkProcessor.Listener listener = new BulkProcessor.Listener() { 52 | @Override 53 | public void beforeBulk(long executionId, BulkRequest request) { 54 | } 55 | 56 | @Override 57 | public void afterBulk(long executionId, BulkRequest request, BulkResponse response) { 58 | } 59 | 60 | @Override 61 | public void afterBulk(long executionId, BulkRequest requst, Throwable failure) { 62 | } 63 | }; 64 | BulkProcessor.Builder builder = BulkProcessor.builder(client, listener) 65 | .setBulkActions(maxActionsPerBulkRequest) 66 | .setConcurrentRequests(maxConcurrentBulkRequests) 67 | .setFlushInterval(flushInterval); 68 | if (maxVolumePerBulkRequest != null) { 69 | builder.setBulkSize(maxVolumePerBulkRequest); 70 | } 71 | this.bulkProcessor = builder.build(); 72 | } 73 | 74 | public ElasticsearchTransportClient index(Map source) { 75 | if (((TransportClient)client).connectedNodes().isEmpty()) { 76 | throw new ElasticsearchIllegalStateException("client is disconnected"); 77 | } 78 | 79 | String index = this.index.indexOf('\'') < 0 ? this.index : getIndexNameDateFormat(this.index).format(new Date()); 80 | bulkProcessor.add(new IndexRequest(index).type(type).create(false).source(source)); 81 | 82 | return this; 83 | } 84 | 85 | public void close() { 86 | bulkProcessor.close(); 87 | client.close(); 88 | } 89 | 90 | private static final ThreadLocal> df = new ThreadLocal>() { 91 | public Map initialValue() { 92 | return new HashMap(); 93 | } 94 | }; 95 | 96 | private SimpleDateFormat getIndexNameDateFormat(String index) { 97 | Map formatters = df.get(); 98 | SimpleDateFormat formatter = formatters.get(index); 99 | if (formatter == null) { 100 | formatter = new SimpleDateFormat(); 101 | formatter.applyPattern(index); 102 | formatters.put(index, formatter); 103 | } 104 | return formatter; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/site/site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | org.apache.maven.skins 5 | maven-fluido-skin 6 | 1.2.1 7 | 8 | 9 | 10 | true 11 | true 12 | 13 | jprante/log4j2-elasticsearch 14 | right 15 | black 16 | 17 | 18 | xbib 19 | true 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/test/java/ElasticsearchAppenderTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Jörg Prante 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import org.junit.Test; 17 | 18 | import org.apache.logging.log4j.Logger; 19 | import org.apache.logging.log4j.LogManager; 20 | 21 | public class ElasticsearchAppenderTest { 22 | 23 | @Test 24 | public void testLog() { 25 | Logger logger = LogManager.getLogger("test"); 26 | logger.info("Hello World"); 27 | } 28 | 29 | @Test 30 | public void testMassiveLog() { 31 | Logger logger = LogManager.getLogger("test"); 32 | for (int i = 0; i < 10000; i++) { 33 | logger.info("Hello World, this is message " + i); 34 | } 35 | } 36 | 37 | @Test 38 | public void testTimeLog() { 39 | Logger logger = LogManager.getLogger("time"); 40 | logger.info("Hello World"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/apache/logging/log4j/nosql/appender/NoSqlAppenderTest.java: -------------------------------------------------------------------------------- 1 | package org.apache.logging.log4j.nosql.appender; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.easymock.EasyMock.createStrictMock; 6 | import static org.easymock.EasyMock.replay; 7 | import static org.easymock.EasyMock.verify; 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertNotNull; 10 | import static org.junit.Assert.assertNull; 11 | 12 | public class NoSqlAppenderTest { 13 | @Test 14 | public void testNoProvider() { 15 | final NoSqlAppender appender = NoSqlAppender.createAppender("myName01", null, null, null, null); 16 | assertNull("The appender should be null.", appender); 17 | } 18 | 19 | @Test 20 | public void testProvider() { 21 | @SuppressWarnings("unchecked") 22 | final NoSqlProvider provider = createStrictMock(NoSqlProvider.class); 23 | replay(provider); 24 | final NoSqlAppender appender = NoSqlAppender.createAppender("myName01", null, null, null, provider); 25 | assertNotNull("The appender should not be null.", appender); 26 | assertEquals("The toString value is not correct.", 27 | "myName01{ manager=noSqlManager{ description=myName01, bufferSize=0, provider=" + provider + " } }", 28 | appender.toString()); 29 | appender.stop(); 30 | verify(provider); 31 | } 32 | 33 | @Test 34 | public void testProviderBuffer() { 35 | @SuppressWarnings("unchecked") 36 | final NoSqlProvider provider = createStrictMock(NoSqlProvider.class); 37 | replay(provider); 38 | final NoSqlAppender appender = NoSqlAppender.createAppender("anotherName02", null, null, "25", provider); 39 | assertNotNull("The appender should not be null.", appender); 40 | assertEquals("The toString value is not correct.", 41 | "anotherName02{ manager=noSqlManager{ description=anotherName02, bufferSize=25, provider=" + provider 42 | + " } }", appender.toString()); 43 | appender.stop(); 44 | verify(provider); 45 | } 46 | } -------------------------------------------------------------------------------- /src/test/java/org/apache/logging/log4j/nosql/appender/NoSqlDatabaseManagerTest.java: -------------------------------------------------------------------------------- 1 | package org.apache.logging.log4j.nosql.appender; 2 | 3 | import org.apache.logging.log4j.Level; 4 | import org.apache.logging.log4j.MarkerManager; 5 | import org.apache.logging.log4j.ThreadContext; 6 | import org.apache.logging.log4j.core.LogEvent; 7 | import org.apache.logging.log4j.core.appender.AppenderLoggingException; 8 | import org.apache.logging.log4j.message.Message; 9 | import org.easymock.Capture; 10 | import org.easymock.IAnswer; 11 | import org.junit.After; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | 15 | import java.io.IOException; 16 | import java.sql.SQLException; 17 | import java.util.Date; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | import static org.easymock.EasyMock.anyInt; 23 | import static org.easymock.EasyMock.capture; 24 | import static org.easymock.EasyMock.createStrictMock; 25 | import static org.easymock.EasyMock.expect; 26 | import static org.easymock.EasyMock.expectLastCall; 27 | import static org.easymock.EasyMock.getCurrentArguments; 28 | import static org.easymock.EasyMock.replay; 29 | import static org.easymock.EasyMock.reset; 30 | import static org.easymock.EasyMock.verify; 31 | import static org.junit.Assert.assertEquals; 32 | import static org.junit.Assert.assertNotNull; 33 | import static org.junit.Assert.assertNull; 34 | import static org.junit.Assert.assertTrue; 35 | import static org.junit.Assert.fail; 36 | 37 | public class NoSqlDatabaseManagerTest { 38 | 39 | private NoSqlConnection, DefaultNoSqlObject> connection; 40 | 41 | private NoSqlProvider, DefaultNoSqlObject>> provider; 42 | 43 | @Before 44 | @SuppressWarnings("unchecked") 45 | public void setUp() { 46 | this.provider = createStrictMock(NoSqlProvider.class); 47 | this.connection = createStrictMock(NoSqlConnection.class); 48 | } 49 | 50 | @After 51 | public void tearDown() { 52 | verify(this.provider, this.connection); 53 | } 54 | 55 | @Test 56 | public void testConnection() { 57 | replay(this.provider, this.connection); 58 | final NoSqlDatabaseManager manager = NoSqlDatabaseManager.getNoSqlDatabaseManager("name", 0, this.provider); 59 | assertNotNull("The manager should not be null.", manager); 60 | try { 61 | verify(this.provider, this.connection); 62 | reset(this.provider, this.connection); 63 | expect(this.provider.getConnection()).andReturn(this.connection); 64 | replay(this.provider, this.connection); 65 | manager.connectAndStart(); 66 | manager.commitAndClose(); 67 | } finally { 68 | try { 69 | manager.release(); 70 | } catch (final Throwable ignore) { 71 | /* */ 72 | } 73 | } 74 | } 75 | 76 | @Test 77 | public void testWriteInternalNotConnected01() { 78 | replay(this.provider, this.connection); 79 | final NoSqlDatabaseManager manager = NoSqlDatabaseManager.getNoSqlDatabaseManager("name", 0, this.provider); 80 | try { 81 | verify(this.provider, this.connection); 82 | reset(this.provider, this.connection); 83 | final LogEvent event = createStrictMock(LogEvent.class); 84 | replay(this.provider, this.connection, event); 85 | try { 86 | manager.writeInternal(event); 87 | fail("Expected AppenderLoggingException."); 88 | } catch (final AppenderLoggingException ignore) { 89 | /* */ 90 | } 91 | verify(event); 92 | } finally { 93 | try { 94 | manager.release(); 95 | } catch (final Throwable ignore) { 96 | /* */ 97 | } 98 | } 99 | } 100 | 101 | @Test 102 | public void testWriteInternalNotConnected02() { 103 | final NoSqlDatabaseManager manager = NoSqlDatabaseManager.getNoSqlDatabaseManager("name", 0, this.provider); 104 | try { 105 | replay(this.provider, this.connection); 106 | manager.startup(); 107 | verify(this.provider, this.connection); 108 | reset(this.provider, this.connection); 109 | expect(this.provider.getConnection()).andReturn(this.connection); 110 | replay(this.provider, this.connection); 111 | manager.connectAndStart(); 112 | verify(this.provider, this.connection); 113 | reset(this.provider, this.connection); 114 | final LogEvent event = createStrictMock(LogEvent.class); 115 | expect(this.connection.isClosed()).andReturn(true); 116 | replay(this.provider, this.connection, event); 117 | try { 118 | manager.writeInternal(event); 119 | fail("Expected AppenderLoggingException."); 120 | } catch (final AppenderLoggingException ignore) { 121 | /* */ 122 | } 123 | } finally { 124 | try { 125 | manager.release(); 126 | } catch (final Throwable ignore) { 127 | /* */ 128 | } 129 | } 130 | } 131 | 132 | @Test 133 | public void testWriteInternal01() { 134 | final NoSqlDatabaseManager manager = NoSqlDatabaseManager.getNoSqlDatabaseManager("name", 0, this.provider); 135 | try { 136 | replay(this.provider, this.connection); 137 | manager.startup(); 138 | verify(this.provider, this.connection); 139 | reset(this.provider, this.connection); 140 | expect(this.provider.getConnection()).andReturn(this.connection); 141 | replay(this.provider, this.connection); 142 | manager.connectAndStart(); 143 | verify(this.provider, this.connection); 144 | reset(this.provider, this.connection); 145 | final Capture>> capture = new Capture>>(); 146 | final LogEvent event = createStrictMock(LogEvent.class); 147 | final Message message = createStrictMock(Message.class); 148 | expect(this.connection.isClosed()).andReturn(false); 149 | expect(this.connection.createObject()).andAnswer(new IAnswer() { 150 | @Override 151 | public DefaultNoSqlObject answer() throws Throwable { 152 | return new DefaultNoSqlObject(); 153 | } 154 | }).atLeastOnce(); 155 | expect(event.getLevel()).andReturn(Level.WARN); 156 | expect(event.getLoggerName()).andReturn("com.foo.NoSQLDbTest.testWriteInternal01"); 157 | expect(event.getMessage()).andReturn(message).times(2); 158 | expect(message.getFormattedMessage()).andReturn("My formatted message 01."); 159 | expect(event.getSource()).andReturn(new StackTraceElement("com.foo.Bar", "testMethod01", "Bar.java", 15)); 160 | expect(event.getMarker()).andReturn(null); 161 | expect(event.getThreadName()).andReturn("MyThread-A"); 162 | expect(event.getTimeMillis()).andReturn(1234567890123L).times(2); 163 | expect(event.getThrown()).andReturn(null); 164 | expect(event.getContextMap()).andReturn(null); 165 | expect(event.getContextStack()).andReturn(null); 166 | this.connection.insertObject(capture(capture)); 167 | expectLastCall(); 168 | replay(this.provider, this.connection, event, message); 169 | manager.writeInternal(event); 170 | final NoSqlObject> inserted = capture.getValue(); 171 | assertNotNull("The inserted value should not be null.", inserted); 172 | final Map object = inserted.unwrap(); 173 | assertNotNull("The unwrapped object should not be null.", object); 174 | assertEquals("The level is not correct.", Level.WARN, object.get("level")); 175 | assertEquals("The logger is not correct.", "com.foo.NoSQLDbTest.testWriteInternal01", 176 | object.get("loggerName")); 177 | assertEquals("The message is not correct.", "My formatted message 01.", object.get("message")); 178 | assertEquals("The thread is not correct.", "MyThread-A", object.get("threadName")); 179 | assertEquals("The millis is not correct.", 1234567890123L, object.get("millis")); 180 | assertEquals("The date is not correct.", 1234567890123L, ((Date) object.get("date")).getTime()); 181 | assertTrue("The source should be a map.", object.get("source") instanceof Map); 182 | @SuppressWarnings("unchecked") 183 | final Map source = (Map) object.get("source"); 184 | assertEquals("The class is not correct.", "com.foo.Bar", source.get("className")); 185 | assertEquals("The method is not correct.", "testMethod01", source.get("methodName")); 186 | assertEquals("The file name is not correct.", "Bar.java", source.get("fileName")); 187 | assertEquals("The line number is not correct.", 15, source.get("lineNumber")); 188 | assertNull("The marker should be null.", object.get("marker")); 189 | assertNull("The thrown should be null.", object.get("thrown")); 190 | assertNull("The context map should be null.", object.get("contextMap")); 191 | assertNull("The context stack should be null.", object.get("contextStack")); 192 | verify(this.provider, this.connection, event, message); 193 | } finally { 194 | try { 195 | manager.release(); 196 | } catch (final Throwable ignore) { 197 | /* */ 198 | } 199 | } 200 | } 201 | 202 | @Test 203 | public void testWriteInternal02() { 204 | final NoSqlDatabaseManager manager = NoSqlDatabaseManager.getNoSqlDatabaseManager("name", 0, this.provider); 205 | try { 206 | replay(this.provider, this.connection); 207 | manager.startup(); 208 | verify(this.provider, this.connection); 209 | reset(this.provider, this.connection); 210 | expect(this.provider.getConnection()).andReturn(this.connection); 211 | replay(this.provider, this.connection); 212 | manager.connectAndStart(); 213 | verify(this.provider, this.connection); 214 | reset(this.provider, this.connection); 215 | final Capture>> capture = new Capture>>(); 216 | final RuntimeException exception = new RuntimeException("This is something cool!"); 217 | final Map context = new HashMap(); 218 | context.put("hello", "world"); 219 | context.put("user", "pass"); 220 | final LogEvent event = createStrictMock(LogEvent.class); 221 | final Message message = createStrictMock(Message.class); 222 | ThreadContext.push("message1"); 223 | ThreadContext.push("stack2"); 224 | final ThreadContext.ContextStack stack = ThreadContext.getImmutableStack(); 225 | ThreadContext.clearStack(); 226 | expect(this.connection.isClosed()).andReturn(false); 227 | expect(this.connection.createObject()).andAnswer(new IAnswer() { 228 | @Override 229 | public DefaultNoSqlObject answer() throws Throwable { 230 | return new DefaultNoSqlObject(); 231 | } 232 | }).atLeastOnce(); 233 | expect(this.connection.createList(anyInt())).andAnswer(new IAnswer() { 234 | @Override 235 | public DefaultNoSqlObject[] answer() throws Throwable { 236 | return new DefaultNoSqlObject[(Integer) getCurrentArguments()[0]]; 237 | } 238 | }); 239 | expect(this.connection.createObject()).andAnswer(new IAnswer() { 240 | @Override 241 | public DefaultNoSqlObject answer() throws Throwable { 242 | return new DefaultNoSqlObject(); 243 | } 244 | }).atLeastOnce(); 245 | expect(event.getLevel()).andReturn(Level.DEBUG); 246 | expect(event.getLoggerName()).andReturn("com.foo.NoSQLDbTest.testWriteInternal02"); 247 | expect(event.getMessage()).andReturn(message).times(2); 248 | expect(message.getFormattedMessage()).andReturn("Another cool message 02."); 249 | expect(event.getSource()).andReturn(new StackTraceElement("com.bar.Foo", "anotherMethod03", "Foo.java", 9)); 250 | expect(event.getMarker()).andReturn(MarkerManager.getMarker("LoneMarker")); 251 | expect(event.getThreadName()).andReturn("AnotherThread-B"); 252 | expect(event.getTimeMillis()).andReturn(987654321564L).times(2); 253 | expect(event.getThrown()).andReturn(exception); 254 | expect(event.getContextMap()).andReturn(context); 255 | expect(event.getContextStack()).andReturn(stack); 256 | this.connection.insertObject(capture(capture)); 257 | expectLastCall(); 258 | replay(this.provider, this.connection, event, message); 259 | manager.writeInternal(event); 260 | final NoSqlObject> inserted = capture.getValue(); 261 | assertNotNull("The inserted value should not be null.", inserted); 262 | final Map object = inserted.unwrap(); 263 | assertNotNull("The unwrapped object should not be null.", object); 264 | assertEquals("The level is not correct.", Level.DEBUG, object.get("level")); 265 | assertEquals("The logger is not correct.", "com.foo.NoSQLDbTest.testWriteInternal02", 266 | object.get("loggerName")); 267 | assertEquals("The message is not correct.", "Another cool message 02.", object.get("message")); 268 | assertEquals("The thread is not correct.", "AnotherThread-B", object.get("threadName")); 269 | assertEquals("The millis is not correct.", 987654321564L, object.get("millis")); 270 | assertEquals("The date is not correct.", 987654321564L, ((Date) object.get("date")).getTime()); 271 | assertTrue("The source should be a map.", object.get("source") instanceof Map); 272 | @SuppressWarnings("unchecked") 273 | final Map source = (Map) object.get("source"); 274 | assertEquals("The class is not correct.", "com.bar.Foo", source.get("className")); 275 | assertEquals("The method is not correct.", "anotherMethod03", source.get("methodName")); 276 | assertEquals("The file name is not correct.", "Foo.java", source.get("fileName")); 277 | assertEquals("The line number is not correct.", 9, source.get("lineNumber")); 278 | assertTrue("The marker should be a map.", object.get("marker") instanceof Map); 279 | @SuppressWarnings("unchecked") 280 | final Map marker = (Map) object.get("marker"); 281 | assertEquals("The marker name is not correct.", "LoneMarker", marker.get("name")); 282 | assertNull("The marker parent should be null.", marker.get("parent")); 283 | assertTrue("The thrown should be a map.", object.get("thrown") instanceof Map); 284 | @SuppressWarnings("unchecked") 285 | final Map thrown = (Map) object.get("thrown"); 286 | assertEquals("The thrown type is not correct.", "java.lang.RuntimeException", thrown.get("type")); 287 | assertEquals("The thrown message is not correct.", "This is something cool!", thrown.get("message")); 288 | assertTrue("The thrown stack trace should be a list.", thrown.get("stackTrace") instanceof List); 289 | @SuppressWarnings("unchecked") 290 | final List> stackTrace = (List>) thrown.get("stackTrace"); 291 | assertEquals("The thrown stack trace length is not correct.", exception.getStackTrace().length, 292 | stackTrace.size()); 293 | for (int i = 0; i < exception.getStackTrace().length; i++) { 294 | final StackTraceElement e1 = exception.getStackTrace()[i]; 295 | final Map e2 = stackTrace.get(i); 296 | assertEquals("Element class name [" + i + "] is not correct.", e1.getClassName(), e2.get("className")); 297 | assertEquals("Element method name [" + i + "] is not correct.", e1.getMethodName(), 298 | e2.get("methodName")); 299 | assertEquals("Element file name [" + i + "] is not correct.", e1.getFileName(), e2.get("fileName")); 300 | assertEquals("Element line number [" + i + "] is not correct.", e1.getLineNumber(), 301 | e2.get("lineNumber")); 302 | } 303 | assertNull("The thrown should have no cause.", thrown.get("cause")); 304 | assertTrue("The context map should be a map.", object.get("contextMap") instanceof Map); 305 | assertEquals("The context map is not correct.", context, object.get("contextMap")); 306 | assertTrue("The context stack should be list.", object.get("contextStack") instanceof List); 307 | assertEquals("The context stack is not correct.", stack.asList(), object.get("contextStack")); 308 | verify(this.provider, this.connection, event, message); 309 | } finally { 310 | try { 311 | manager.release(); 312 | } catch (final Throwable ignore) { 313 | /* */ 314 | } 315 | } 316 | } 317 | 318 | @Test 319 | public void testWriteInternal03() { 320 | final NoSqlDatabaseManager manager = NoSqlDatabaseManager.getNoSqlDatabaseManager("name", 0, this.provider); 321 | try { 322 | replay(this.provider, this.connection); 323 | manager.startup(); 324 | verify(this.provider, this.connection); 325 | reset(this.provider, this.connection); 326 | expect(this.provider.getConnection()).andReturn(this.connection); 327 | replay(this.provider, this.connection); 328 | manager.connectAndStart(); 329 | verify(this.provider, this.connection); 330 | reset(this.provider, this.connection); 331 | final Capture>> capture = new Capture>>(); 332 | final IOException exception1 = new IOException("This is the cause."); 333 | final SQLException exception2 = new SQLException("This is the result.", exception1); 334 | final Map context = new HashMap(); 335 | context.put("hello", "world"); 336 | context.put("user", "pass"); 337 | final LogEvent event = createStrictMock(LogEvent.class); 338 | final Message message = createStrictMock(Message.class); 339 | ThreadContext.push("message1"); 340 | ThreadContext.push("stack2"); 341 | final ThreadContext.ContextStack stack = ThreadContext.getImmutableStack(); 342 | ThreadContext.clearStack(); 343 | expect(this.connection.isClosed()).andReturn(false); 344 | expect(this.connection.createObject()).andAnswer(new IAnswer() { 345 | @Override 346 | public DefaultNoSqlObject answer() throws Throwable { 347 | return new DefaultNoSqlObject(); 348 | } 349 | }).atLeastOnce(); 350 | expect(this.connection.createList(anyInt())).andAnswer(new IAnswer() { 351 | @Override 352 | public DefaultNoSqlObject[] answer() throws Throwable { 353 | return new DefaultNoSqlObject[(Integer) getCurrentArguments()[0]]; 354 | } 355 | }); 356 | expect(this.connection.createObject()).andAnswer(new IAnswer() { 357 | @Override 358 | public DefaultNoSqlObject answer() throws Throwable { 359 | return new DefaultNoSqlObject(); 360 | } 361 | }).atLeastOnce(); 362 | expect(this.connection.createList(anyInt())).andAnswer(new IAnswer() { 363 | @Override 364 | public DefaultNoSqlObject[] answer() throws Throwable { 365 | return new DefaultNoSqlObject[(Integer) getCurrentArguments()[0]]; 366 | } 367 | }); 368 | expect(this.connection.createObject()).andAnswer(new IAnswer() { 369 | @Override 370 | public DefaultNoSqlObject answer() throws Throwable { 371 | return new DefaultNoSqlObject(); 372 | } 373 | }).atLeastOnce(); 374 | expect(event.getLevel()).andReturn(Level.DEBUG); 375 | expect(event.getLoggerName()).andReturn("com.foo.NoSQLDbTest.testWriteInternal02"); 376 | expect(event.getMessage()).andReturn(message).times(2); 377 | expect(message.getFormattedMessage()).andReturn("Another cool message 02."); 378 | expect(event.getSource()).andReturn(new StackTraceElement("com.bar.Foo", "anotherMethod03", "Foo.java", 9)); 379 | expect(event.getMarker()).andReturn( 380 | MarkerManager.getMarker("AnotherMarker").addParents(MarkerManager.getMarker("Parent1").addParents(MarkerManager.getMarker("GrandParent1")), 381 | MarkerManager.getMarker("Parent2"))); 382 | expect(event.getThreadName()).andReturn("AnotherThread-B"); 383 | expect(event.getTimeMillis()).andReturn(987654321564L).times(2); 384 | expect(event.getThrown()).andReturn(exception2); 385 | expect(event.getContextMap()).andReturn(context); 386 | expect(event.getContextStack()).andReturn(stack); 387 | this.connection.insertObject(capture(capture)); 388 | expectLastCall(); 389 | replay(this.provider, this.connection, event, message); 390 | manager.writeInternal(event); 391 | final NoSqlObject> inserted = capture.getValue(); 392 | assertNotNull("The inserted value should not be null.", inserted); 393 | final Map object = inserted.unwrap(); 394 | assertNotNull("The unwrapped object should not be null.", object); 395 | assertEquals("The level is not correct.", Level.DEBUG, object.get("level")); 396 | assertEquals("The logger is not correct.", "com.foo.NoSQLDbTest.testWriteInternal02", 397 | object.get("loggerName")); 398 | assertEquals("The message is not correct.", "Another cool message 02.", object.get("message")); 399 | assertEquals("The thread is not correct.", "AnotherThread-B", object.get("threadName")); 400 | assertEquals("The millis is not correct.", 987654321564L, object.get("millis")); 401 | assertEquals("The date is not correct.", 987654321564L, ((Date) object.get("date")).getTime()); 402 | assertTrue("The source should be a map.", object.get("source") instanceof Map); 403 | @SuppressWarnings("unchecked") 404 | final Map source = (Map) object.get("source"); 405 | assertEquals("The class is not correct.", "com.bar.Foo", source.get("className")); 406 | assertEquals("The method is not correct.", "anotherMethod03", source.get("methodName")); 407 | assertEquals("The file name is not correct.", "Foo.java", source.get("fileName")); 408 | assertEquals("The line number is not correct.", 9, source.get("lineNumber")); 409 | assertTrue("The marker should be a map.", object.get("marker") instanceof Map); 410 | @SuppressWarnings("unchecked") 411 | final Map marker = (Map) object.get("marker"); 412 | assertEquals("The marker name is not correct.", "AnotherMarker", marker.get("name")); 413 | assertTrue("The marker parents should be a list.", marker.get("parents") instanceof List); 414 | @SuppressWarnings("unchecked") 415 | final List markerParents = (List) marker.get("parents"); 416 | assertEquals("The marker parents should contain two parents", 2, markerParents.size()); 417 | 418 | assertTrue("The marker parents[0] should be a map.", markerParents.get(0) instanceof Map); 419 | @SuppressWarnings("unchecked") 420 | final Map parent1 = (Map) markerParents.get(0); 421 | assertEquals("The first marker parent name is not correct.", "Parent1", parent1.get("name")); 422 | 423 | assertTrue("The marker parents[1] should be a map.", markerParents.get(1) instanceof Map); 424 | @SuppressWarnings("unchecked") 425 | final Map parent2 = (Map) markerParents.get(1); 426 | assertEquals("The second marker parent name is not correct.", "Parent2", parent2.get("name")); 427 | assertNull("The second marker should have no parent.", parent2.get("parent")); 428 | assertTrue("The parent1 parents should be a list.", parent1.get("parents") instanceof List); 429 | @SuppressWarnings("unchecked") 430 | final List parent1Parents = (List) parent1.get("parents"); 431 | assertEquals("The parent1 parents should have only one parent", 1, parent1Parents.size()); 432 | 433 | assertTrue("The parent1Parents[0] should be a map.", parent1Parents.get(0) instanceof Map); 434 | @SuppressWarnings("unchecked") 435 | final Map parent1parent = (Map) parent1Parents.get(0); 436 | assertEquals("The first parent1 parent name is not correct.", "GrandParent1", parent1parent.get("name")); 437 | assertNull("The parent1parent marker should have no parent.", parent1parent.get("parent")); 438 | assertTrue("The thrown should be a map.", object.get("thrown") instanceof Map); 439 | @SuppressWarnings("unchecked") 440 | final Map thrown = (Map) object.get("thrown"); 441 | assertEquals("The thrown type is not correct.", "java.sql.SQLException", thrown.get("type")); 442 | assertEquals("The thrown message is not correct.", "This is the result.", thrown.get("message")); 443 | assertTrue("The thrown stack trace should be a list.", thrown.get("stackTrace") instanceof List); 444 | @SuppressWarnings("unchecked") 445 | final List> stackTrace = (List>) thrown.get("stackTrace"); 446 | assertEquals("The thrown stack trace length is not correct.", exception2.getStackTrace().length, 447 | stackTrace.size()); 448 | for (int i = 0; i < exception2.getStackTrace().length; i++) { 449 | final StackTraceElement e1 = exception2.getStackTrace()[i]; 450 | final Map e2 = stackTrace.get(i); 451 | assertEquals("Element class name [" + i + "] is not correct.", e1.getClassName(), e2.get("className")); 452 | assertEquals("Element method name [" + i + "] is not correct.", e1.getMethodName(), 453 | e2.get("methodName")); 454 | assertEquals("Element file name [" + i + "] is not correct.", e1.getFileName(), e2.get("fileName")); 455 | assertEquals("Element line number [" + i + "] is not correct.", e1.getLineNumber(), 456 | e2.get("lineNumber")); 457 | } 458 | assertTrue("The thrown cause should be a map.", thrown.get("cause") instanceof Map); 459 | @SuppressWarnings("unchecked") 460 | final Map cause = (Map) thrown.get("cause"); 461 | assertEquals("The cause type is not correct.", "java.io.IOException", cause.get("type")); 462 | assertEquals("The cause message is not correct.", "This is the cause.", cause.get("message")); 463 | assertTrue("The cause stack trace should be a list.", cause.get("stackTrace") instanceof List); 464 | @SuppressWarnings("unchecked") 465 | final List> causeStackTrace = (List>) cause.get("stackTrace"); 466 | assertEquals("The cause stack trace length is not correct.", exception1.getStackTrace().length, 467 | causeStackTrace.size()); 468 | for (int i = 0; i < exception1.getStackTrace().length; i++) { 469 | final StackTraceElement e1 = exception1.getStackTrace()[i]; 470 | final Map e2 = causeStackTrace.get(i); 471 | assertEquals("Element class name [" + i + "] is not correct.", e1.getClassName(), e2.get("className")); 472 | assertEquals("Element method name [" + i + "] is not correct.", e1.getMethodName(), 473 | e2.get("methodName")); 474 | assertEquals("Element file name [" + i + "] is not correct.", e1.getFileName(), e2.get("fileName")); 475 | assertEquals("Element line number [" + i + "] is not correct.", e1.getLineNumber(), 476 | e2.get("lineNumber")); 477 | } 478 | assertNull("The cause should have no cause.", cause.get("cause")); 479 | assertTrue("The context map should be a map.", object.get("contextMap") instanceof Map); 480 | assertEquals("The context map is not correct.", context, object.get("contextMap")); 481 | assertTrue("The context stack should be list.", object.get("contextStack") instanceof List); 482 | assertEquals("The context stack is not correct.", stack.asList(), object.get("contextStack")); 483 | verify(this.provider, this.connection, event, message); 484 | } finally { 485 | try { 486 | manager.release(); 487 | } catch (final Throwable ignore) { 488 | /* */ 489 | } 490 | } 491 | } 492 | } -------------------------------------------------------------------------------- /src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | --------------------------------------------------------------------------------