├── .github └── dependabot.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── SECURITY.md ├── THIRD_PARTY_LICENSES.txt ├── images └── screenshot.png ├── pom.xml ├── samples ├── WebLogicFileLoggingExporter.yaml ├── WebLogicLoggingExporter.yaml └── run-elk-local.sh ├── sonar-project.properties └── src ├── main ├── java │ └── weblogic │ │ └── logging │ │ └── exporter │ │ ├── LogExportHandler.java │ │ ├── Result.java │ │ ├── Startup.java │ │ ├── WebLogicLogFormatter.java │ │ └── config │ │ ├── Config.java │ │ ├── ConfigurationException.java │ │ ├── FilterConfig.java │ │ ├── MapUtils.java │ │ └── YamlParserException.java └── main.iml └── test ├── java └── weblogic │ └── logging │ └── exporter │ ├── ResultTest.java │ └── config │ ├── ConfigTest.java │ ├── FilterConfigTest.java │ └── MapUtilsTest.java └── resources ├── bad.yaml ├── config1.yaml ├── config2.yaml ├── config3.yaml └── emptyConfig.yaml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "maven" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | *iml 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to this repository 2 | 3 | We welcome your contributions! There are multiple ways to contribute. 4 | 5 | ## Opening issues 6 | 7 | For bugs or enhancement requests, please file a GitHub issue unless it's 8 | security related. When filing a bug remember that the better written the bug is, 9 | the more likely it is to be fixed. If you think you've found a security 10 | vulnerability, do not raise a GitHub issue and follow the instructions in our 11 | [security policy](./SECURITY.md). 12 | 13 | ## Contributing code 14 | 15 | We welcome your code contributions. Before submitting code via a pull request, 16 | you will need to have signed the [Oracle Contributor Agreement][OCA] (OCA) and 17 | your commits need to include the following line using the name and e-mail 18 | address you used to sign the OCA: 19 | 20 | ```text 21 | Signed-off-by: Your Name 22 | ``` 23 | 24 | This can be automatically added to pull requests by committing with `--sign-off` 25 | or `-s`, e.g. 26 | 27 | ```text 28 | git commit --signoff 29 | ``` 30 | 31 | Only pull requests from committers that can be verified as having signed the OCA 32 | can be accepted. 33 | 34 | ## Pull request process 35 | 36 | 1. Ensure there is an issue created to track and discuss the fix or enhancement 37 | you intend to submit. 38 | 1. Fork this repository 39 | 1. Create a branch in your fork to implement the changes. We recommend using 40 | the issue number as part of your branch name, e.g. `1234-fixes` 41 | 1. Ensure that any documentation is updated with the changes that are required 42 | by your change. 43 | 1. Ensure that any samples are updated if the base image has been changed. 44 | 1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly 45 | what your changes are meant to do and provide simple steps on how to validate 46 | your changes. Ensure that you reference the issue you created as well. 47 | 1. We will assign the pull request to 2-3 people for review before it is merged. 48 | 49 | ## Code of conduct 50 | 51 | Follow the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule). If you'd 52 | like more specific guidelines, see the [Contributor Covenant Code of Conduct][COC]. 53 | 54 | [OCA]: https://oca.opensource.oracle.com 55 | [COC]: https://www.contributor-covenant.org/version/1/4/code-of-conduct/ 56 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Oracle and/or its affiliates. 2 | 3 | The Universal Permissive License (UPL), Version 1.0 4 | 5 | Subject to the condition set forth below, permission is hereby granted to any 6 | person obtaining a copy of this software, associated documentation and/or data 7 | (collectively the "Software"), free of charge and under any and all copyright 8 | rights in the Software, and any and all patent rights owned or freely 9 | licensable by each licensor hereunder covering either (i) the unmodified 10 | Software as contributed to or provided by such licensor, or (ii) the Larger 11 | Works (as defined below), to deal in both 12 | 13 | (a) the Software, and 14 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 15 | one is included with the Software (each a "Larger Work" to which the Software 16 | is contributed by such licensors), 17 | 18 | without restriction, including without limitation the rights to copy, create 19 | derivative works of, display, perform, and distribute the Software and make, 20 | use, sell, offer for sale, import, export, have made, and have sold the 21 | Software and the Larger Work(s), and to sublicense the foregoing rights on 22 | either these or other terms. 23 | 24 | This license is subject to the following condition: 25 | The above copyright notice and either this complete permission notice or at 26 | a minimum a reference to the UPL must be included in all copies or 27 | substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35 | SOFTWARE. 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebLogic Logging Exporter 2 | 3 | **NOTE**: The WebLogic Logging Exporter project has been archived. Now, the repository is read-only 4 | and all issues, pull requests, code, labels, milestones, and such, also have become read-only. Contributors with 5 | access to the repository only can fork or star the project. 6 | 7 | Users are encouraged to use Fluentd or Logstash. If you use Fluentd to export logs to Elasticsearch or OpenSearch, 8 | then you may be interested in the WebLogic Kubernetes Operator documentation that describes how you can use Fluentd to export 9 | [WebLogic logs to Elasticsearch](https://oracle.github.io/weblogic-kubernetes-operator/samples/elastic-stack/weblogic-domain/). 10 | 11 | The goal of this project is to provide an easy to configure, robust, and production-ready solution to access 12 | WLS log information through Elasticsearch and Kibana. 13 | 14 | The WebLogic Logging Exporter adds a log event handler to WebLogic Server, 15 | such that WebLogic Server logs can be integrated into [Elastic Stack](https://www.elastic.co/products) 16 | in Kubernetes directly, by using the [Elasticsearch](https://www.elastic.co/products/elasticsearch) REST API. 17 | 18 | The current version of the WebLogic Logging Exporter is 1.0.1, which was released on Wednesday, January 27, 2021. 19 | This version supports pushing logs into Elasticsearch using the REST API. 20 | 21 | The following features are planned for the next few releases: 22 | 23 | * Push logs into a Fluentd aggregator using the REST API. 24 | * Write logs in JSON format into the file system so that they could be collected and published by a 25 | sidecar, for example, Fluentd or Logstash. 26 | * Provide the ability to publish other logs (for example, other than the server logs). 27 | 28 | ## Contents 29 | 30 | * How to [download the release](#download-the-release) 31 | * How to [build from source](#building-from-source) 32 | * How to [install](#installation) the WebLogic Logging exporter 33 | * How to [run Elasticsearch and Kibana locally for testing](#running-elasticsearch-and-kibana-locally-for-testing) 34 | * How to [contribute](#contributing) to this project 35 | * [License](#license) information 36 | 37 | 38 | ## Download the release 39 | 40 | You can download the WebLogic Logging Exporter already compiled for you from the 41 | [releases page](https://github.com/oracle/weblogic-logging-exporter/releases). 42 | 43 | ## Building from source 44 | 45 | If you prefer, you can build the WebLogic Logging Exporter from the source code. To do this, you will 46 | need access to some WebLogic Server libraries. There are two ways to get these libraries: 47 | 48 | * Populate your local Maven repository with the required files from a local WebLogic Server installation 49 | using the Oracle Maven Synchronization plugin, or 50 | * Use the Oracle Maven repository to download them as part of your build; this requires registration and 51 | configuring your local Maven installation with the appropriate authentication details. 52 | 53 | ### Populating your local Maven repository from a local WebLogic Server installation 54 | 55 | You can use the Oracle Maven Synchronization plugin, which is included in your WebLogic Server installation, 56 | to install the necessary dependencies into your local Maven repository. 57 | 58 | There are two steps: 59 | 60 | * Install the Oracle Maven Synchronization plugin. 61 | * Run the `push` goal to populate your local Maven repository from your WebLogic Server installation. 62 | 63 | #### Installing the Oracle Maven Synchronization plugin 64 | 65 | To install the plugin, navigate to your WebLogic Server installation, then enter the commands (this example 66 | assumes you installed WebLogic Server in `/u01/wlshome`): 67 | 68 | ``` 69 | cd /u01/wlshome/oracle_common/plugins/maven/com/oracle/oracle-maven-sync/12.2.1 70 | mvn install:install-file -DpomFile=oracle-maven-sync-12.2.1.pom -Dfile=oracle-maven-sync-12.2.1.jar 71 | ``` 72 | 73 | #### Popoulating your local Maven repository 74 | 75 | To populate your local Maven repository from your WebLogic Server installation, enter this command: 76 | 77 | ``` 78 | mvn com.oracle.maven:oracle-maven-sync:push -DoracleHome=/u01/wlshome 79 | ``` 80 | 81 | You can verify the dependencies were installed by looking in your local Maven repository which is 82 | normally located at `~/.m2/repository/com/oracle/weblogic`. 83 | 84 | 85 | ### Using the Oracle Maven repository 86 | 87 | **Note**: If you populated your local repository using the Oracle Maven Synchronization plugin, then this 88 | step is *not* required. 89 | 90 | To access the Oracle Maven repository, refer to the documentation 91 | [available here](https://docs.oracle.com/middleware/1213/core/MAVEN/config_maven_repo.htm#MAVEN9010). 92 | 93 | 94 | ### Building the WebLogic Logging Exporter 95 | 96 | To build the WebLogic Logging Exporter, clone the project from GitHub and then build it with Maven: 97 | 98 | ``` 99 | git clone git@orahub.oraclecorp.com:oracle/wls-logging-exporter.git 100 | mvn install 101 | ``` 102 | 103 | The `weblogic-logging-exporter.jar` will be available under the `target` directory. 104 | 105 | ## Installation 106 | 107 | This section outlines the steps that are required to add the WebLogic Logging Exporter to WebLogic Server. 108 | 109 | 1. Download or build the WebLogic Logging Exporter as described above. 110 | 111 | 1. Copy the `weblogic-logging-exporter.jar` into a suitable location, e.g. into your domain directory. 112 | 113 | 1. Add a startup class to your domain configuration. 114 | 115 | * In the Administration Console, navigate to "Environment" then "Startup and Shutdown classes" in the main menu. 116 | * Add a new Startup class. You may choose any descriptive name and the class name must be 117 | `weblogic.logging.exporter.Startup`. 118 | * Target the startup class to each server that you want to export logs from. 119 | 120 | You can verify this by checking for the update in your `config.xml` which should be similar to this example: 121 | 122 | ``` 123 | 124 | LoggingExporterStartupClass 125 | AdminServer 126 | weblogic.logging.exporter.Startup 127 | 128 | ``` 129 | 130 | 1. Add `weblogic-logging-exporter.jar` and `snakeyaml-1.27.jar` to your classpath. 131 | 132 | This project requires `snakeyaml` to parse the YAML configuration file. If you built the project locally, 133 | you can find this JAR file in your local maven repository at `~/.m2/repository/org/yaml/snakeyaml/1.27/snakeyaml-1.27.jar`. 134 | Otherwise, you can download it from [Maven Central](https://search.maven.org/artifact/org.yaml/snakeyaml/1.27/bundle). 135 | 136 | Place the file(s) in a suitable location, e.g. your domain directory. 137 | 138 | Update the server classpath to include these file(s). This can be done by adding a statement to the end of your 139 | `setDomainEnv.sh` script in your domain's `bin` directory as follows (this example assumes your domain 140 | directory is `/u01/base_domain`): 141 | 142 | ``` 143 | export CLASSPATH="/u01/base_domain/weblogic-logging-exporter.jar:/u01/base_domain/snakeyaml-1.27.jar:$CLASSPATH" 144 | ``` 145 | 146 | 1. Create a configuration file for the WebLogic Logging Exporter. 147 | 148 | There are two options currently - the version 1.x configuration, or the new version 2.x configuration - please 149 | note that the 2.x configuration is `alpha` and therefore subject to change as we get close to the 2.0 release. 150 | 151 | a. Version 1.x configuration 152 | 153 | Create a file named `WebLogicLoggingExporter.yaml` in your domain's `config` directory. You can copy the 154 | [sample provided in this project](samples/WebLogicLoggingExporter.yaml) as a starting point. That sample 155 | contains details of all of the available configuration options. A completed configuration file might look 156 | like this: 157 | 158 | ``` 159 | publishHost: localhost 160 | publishPort: 9200 161 | domainUID: domain1 162 | weblogicLoggingExporterEnabled: true 163 | weblogicLoggingIndexName: domain1-wls 164 | weblogicLoggingExporterSeverity: Notice 165 | weblogicLoggingExporterBulkSize: 1 166 | weblogicLoggingExporterFilters: 167 | - filterExpression: 'severity > Warning' 168 | ``` 169 | 170 | Note that you must give a unique `domainUID` to each domain. This value is used to filter logs by domain when you 171 | send the logs from multiple domains to the same Elasticsearch server. If you are using the WebLogic Kubernetes 172 | Operator, it is strongly recommended that you use the same `domainUID` value that you use for the domain. 173 | 174 | It is also strongly recommended that you consider using a different Elastcsearch index name for each domain. 175 | 176 | b. Version 2.x configuration 177 | 178 | If you prefer to place the configuration file in a different location, you can set the environment variable 179 | `WEBLOGIC_LOGGING_EXPORTER_CONFIG_FILE` to point to the location of the file. 180 | 181 | If you want to write the JSON logs to a file instead of sending it elasticsearch directly use the following configuration 182 | [file](samples/WebLogicFileLoggingExporter.yaml) and adjust it to your needs. Make sure to rename it to WebLogicLoggingExporter.yaml. 183 | 184 | 6. Restart the servers to activate the changes. After restarting the servers, they will load the WebLogic 185 | Logging Exporter and start sending their logs to the specified Elasticsearch instance. You can then 186 | access them in Kibana as shown in the example below. You will need to create an index first and then go to 187 | the visualization page. 188 | 189 | ![Kibana screenshot](images/screenshot.png) 190 | 191 | 192 | You can also use a curl command similar to the following example to verify that logs have been posted to Elasticsearch. 193 | The default index name is `wls`, and `docs.count` should be greater than zero indicating that log entries 194 | are being sent to Elasticsearch. 195 | 196 | ``` 197 | $ curl "localhost:9200/_cat/indices?v" 198 | health status index uuid pri rep docs.count docs.deleted store.size pri.store.size 199 | yellow open wls q4Q2v2dXTBOyYsHZMdDe3H 5 1 23 0 101kb 101kb 200 | ``` 201 | 202 | ## Running Elasticsearch and Kibana locally for testing 203 | 204 | If you wish to test on your local machine, a [sample](samples/run-elk-local.sh) is provided to run Elasticsearch 205 | and Kibana in Docker on your local machine. 206 | 207 | 208 | ## Contributing 209 | 210 | This project welcomes contributions from the community. Before submitting a pull 211 | request, please [review our contribution guide](./CONTRIBUTING.md). 212 | 213 | ## Security 214 | 215 | Please consult the [security guide](./SECURITY.md) for our responsible security 216 | vulnerability disclosure process. 217 | 218 | ## License 219 | 220 | Copyright (c) 2017, 2021 Oracle and/or its affiliates. 221 | 222 | Released under the Universal Permissive License v1.0 as shown at 223 | . 224 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting security vulnerabilities 2 | 3 | Oracle values the independent security research community and believes that 4 | responsible disclosure of security vulnerabilities helps us ensure the security 5 | and privacy of all our users. 6 | 7 | Please do NOT raise a GitHub Issue to report a security vulnerability. If you 8 | believe you have found a security vulnerability, please submit a report to 9 | [secalert_us@oracle.com][1] preferably with a proof of concept. Please review 10 | some additional information on [how to report security vulnerabilities to Oracle][2]. 11 | We encourage people who contact Oracle Security to use email encryption using 12 | [our encryption key][3]. 13 | 14 | We ask that you do not use other channels or contact the project maintainers 15 | directly. 16 | 17 | Non-vulnerability related security issues including ideas for new or improved 18 | security features are welcome on GitHub Issues. 19 | 20 | ## Security updates, alerts and bulletins 21 | 22 | Security updates will be released on a regular cadence. Many of our projects 23 | will typically release security fixes in conjunction with the 24 | [Oracle Critical Patch Update][3] program. Security updates are released on the 25 | Tuesday closest to the 17th day of January, April, July and October. A pre-release 26 | announcement will be published on the Thursday preceding each release. Additional 27 | information, including past advisories, is available on our [security alerts][4] 28 | page. 29 | 30 | ## Security-related information 31 | 32 | We will provide security related information such as a threat model, considerations 33 | for secure use, or any known security issues in our documentation. Please note 34 | that labs and sample code are intended to demonstrate a concept and may not be 35 | sufficiently hardened for production use. 36 | 37 | [1]: mailto:secalert_us@oracle.com 38 | [2]: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html 39 | [3]: https://www.oracle.com/security-alerts/encryptionkey.html 40 | [4]: https://www.oracle.com/security-alerts/ 41 | -------------------------------------------------------------------------------- /THIRD_PARTY_LICENSES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/weblogic-logging-exporter/82d51da262a5279eb7c3f39d753de1fecfeab290/THIRD_PARTY_LICENSES.txt -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/weblogic-logging-exporter/82d51da262a5279eb7c3f39d753de1fecfeab290/images/screenshot.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 4.0.0 8 | 9 | oracle.kubernetes 10 | weblogic-logging-exporter 11 | jar 12 | 1.0.1 13 | 14 | WebLogic Logging Exporter 15 | An exporter that integrates WebLogic Server logs into ElasticSearch and Kibana 16 | 17 | https://github.com/oracle/weblogic-logging-exporter 18 | 2018 19 | 20 | 21 | Oracle Universal Permissive License, Version 1.0 22 | http://oss.oracle.com/licenses/upl 23 | 24 | 25 | 26 | 51 | 52 | 53 | weblogic-logging-exporter 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-compiler-plugin 58 | 3.10.1 59 | 60 | 1.8 61 | 1.8 62 | 63 | 64 | 65 | 66 | maven-surefire-plugin 67 | 2.22.2 68 | 69 | 73 | 74 | 75 | 76 | 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-failsafe-plugin 90 | 2.22.2 91 | 92 | 93 | 94 | org.jacoco 95 | jacoco-maven-plugin 96 | 0.8.8 97 | 98 | 99 | 100 | prepare-agent 101 | 102 | 103 | 104 | 105 | report 106 | test 107 | 108 | report 109 | 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-assembly-plugin 116 | 3.4.2 117 | 118 | 119 | package 120 | 121 | single 122 | 123 | 124 | 125 | 126 | 127 | jar-with-dependencies 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | javax 137 | javaee-api 138 | 8.0.1 139 | provided 140 | 141 | 142 | com.oracle.weblogic 143 | com.oracle.weblogic.logging 144 | 12.2.1-3-0 145 | provided 146 | 147 | 148 | com.oracle.weblogic 149 | com.bea.core.logging 150 | 12.2.1-3-0 151 | provided 152 | 153 | 154 | com.oracle.weblogic 155 | com.bea.core.logging.debug 156 | 12.2.1-3-0 157 | provided 158 | 159 | 160 | com.oracle.weblogic 161 | com.bea.core.diagnostics.core 162 | 12.2.1-3-0 163 | provided 164 | 165 | 166 | com.oracle.weblogic 167 | com.bea.core.diagnostics.logging 168 | 12.2.1-3-0 169 | provided 170 | 171 | 172 | com.oracle.weblogic 173 | com.bea.core.i18n 174 | 12.2.1-3-0 175 | provided 176 | 177 | 178 | com.oracle.weblogic 179 | com.bea.core.repackaged.asm 180 | 12.2.1-3-0 181 | provided 182 | 183 | 184 | com.oracle.weblogic 185 | com.bea.core.diagnostics.query 186 | 12.2.1-3-0 187 | provided 188 | 189 | 190 | org.junit.jupiter 191 | junit-jupiter-api 192 | 5.9.0 193 | test 194 | 195 | 196 | org.junit.jupiter 197 | junit-jupiter-engine 198 | 5.9.0 199 | test 200 | 201 | 202 | org.hamcrest 203 | hamcrest-junit 204 | 2.0.0.0 205 | test 206 | 207 | 208 | org.yaml 209 | snakeyaml 210 | 1.33 211 | 212 | 213 | co.elastic.logging 214 | jul-ecs-formatter 215 | 1.5.0 216 | 217 | 218 | org.slf4j 219 | slf4j-jdk14 220 | 2.0.6 221 | 222 | 223 | org.antlr 224 | antlr-complete 225 | 3.5.3 226 | 227 | test 228 | 229 | 230 | 231 | 232 | 233 | org.jacoco 234 | jacoco-maven-plugin 235 | 236 | 237 | 238 | 239 | report 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /samples/WebLogicFileLoggingExporter.yaml: -------------------------------------------------------------------------------- 1 | # turn off the elasticsearch output 2 | weblogicLoggingExporterEnabled: false 3 | 4 | # configure json logging output 5 | writeToFileEnabled: true 6 | # %g is necessary for rollback of log files. 7 | outputFile: '/var/log/oracle/json-logging%g.log' 8 | # Max open files 9 | maxRollbackFiles: 3 10 | # Max file size in bytes, this is 50 MB 11 | maxFileSize: 52428800 12 | # Should the log file be appended to when the new logger is created 13 | appendToFile: true 14 | # Optional configuration for specifying which levels get logged to the json log file. 15 | # fileLoggingLogLevel: INFO 16 | -------------------------------------------------------------------------------- /samples/WebLogicLoggingExporter.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | # This is a sample configuration file for weblogic Logging Exporter 5 | # The exporter will look for the file specified by the system variable "WEBLOGIC_LOGGING_EXPORTER_CONFIG_FILE", 6 | # if this is not specified, will search by the default name under domain config directory. 7 | # By default, the logging exporter will look for this file "WebLogicLoggingExporter.yaml" under the 8 | # domain config directory. 9 | # If file doesn't exist, will use all the defaults. 10 | 11 | # The index name for weblogic logs used in elastic search. By default it is set to "wls"; 12 | # Elastic Search restricts the index name to be lower case only. Exporter will automatically convert this to all lower case. 13 | # and gives a warning. 14 | weblogicLoggingIndexName: wls 15 | 16 | # The host that Elastic Search is running on. Defaults to localhost. 17 | publishHost: localhost 18 | 19 | # The port that Elastic Search is listening to. Defaults to 9200. 20 | publishPort: 9200 21 | 22 | # The "domainUID" used to uniquely identify this domain. This is used to filter logs in Kibana by the domain they came 23 | # from. You should use a unique value for each domain. If you are using the WebLogic Kubernetes Operator, it is 24 | # strongly recommended you use the same value as in the domain custom resource. 25 | domainUID: domain1 26 | 27 | # Decide if this exporter should be enabled. Default is true. If set to false, nothing will be logged to Elastic Search. 28 | weblogicLoggingExporterEnabled: true 29 | 30 | # The minimum severity of log messages that should be exported to Elastic Search. 31 | # Messages with a lower severity than the specified value will not be exported. 32 | # Note that this is for all the messages that's published to domain.log. By Default, domain.log includes all messages that is of severity Notice or higher. 33 | # This means that if you set weblogicLoggingExporterSeverity to lower, eg. INFO, it may not be exported depending if you have changed the default value of 34 | # Domain log to log at a lower severity. 35 | # Possible value includes: Debug, Info, Notice, Warning, Error, Critical, Alert, Emergency, Off. 36 | # If this is set to Off, ALL messages from domain log will be exported. 37 | weblogicLoggingExporterSeverity: Notice 38 | 39 | # Exports the messages to Elastic Search in bulk mode. 40 | # When this is not specified, by default, the exporter will not do any batching. Each messages will be posted to Elastic Search immediately 41 | # as it comes in. 42 | # If specified, the post will happen after the number of messages specified is gathered and will be posted in one batch. 43 | # batch size must be greater than 0 44 | weblogicLoggingExporterBulkSize: 2 45 | 46 | # Filters that will be applied to all the messages. 47 | # More than 1 filter can be specified, messages MUST satisfy all the filter condition before it will be exported. 48 | # If FilterServers is specified, then that filter will be applied only to messages that is generated by the specified servers. 49 | # More than one server is allowed. 50 | # If no FilterServers is specified, it will be applied to all messages. 51 | # WLDF includes a query language for constructing log filter expressions. 52 | # The syntax is a small and simplified subset of SQL syntax. 53 | # refer to https://docs.oracle.com/middleware/1221/wls/WLDFC/appendix_query.htm#WLDFC336 on how to create a log filter expressions. 54 | # 55 | weblogicLoggingExporterFilters: 56 | - FilterExpression: MSGID != 'BEA-000449' 57 | 58 | # example of filter, this filter will only be applied to messages that is generated by Server-0 59 | #- FilterExpression: "(MACHINE = 'machine1') OR (SEVERITY >= Warning)" 60 | # FilterServers: Server-0 61 | 62 | -------------------------------------------------------------------------------- /samples/run-elk-local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) 2018, 2021, Oracle and/or its affiliates. 3 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 4 | 5 | # start elasticsearch... 6 | docker run -d \ 7 | --name elasticsearch \ 8 | -p 9200:9200 \ 9 | -p 9300:9300 \ 10 | -e "discovery.type=single-node" \ 11 | docker.elastic.co/elasticsearch/elasticsearch:6.2.2 12 | 13 | # start kibana... 14 | docker run -d \ 15 | --name kibana \ 16 | -p 5601:5601 \ 17 | --link elasticsearch:elasticsearch \ 18 | -e "ELASTICSEARCH_URL=http://elasticsearch:9200" \ 19 | docker.elastic.co/kibana/kibana:6.2.2 20 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | # Required metadata 5 | sonar.projectKey=weblogic-logging-exporter 6 | sonar.projectName=WebLogic Logging Exporter 7 | sonar.projectVersion=1.0.1 8 | 9 | # Comma-separated paths to directories with sources (required) 10 | sonar.sources=src/main/java 11 | sonar.java.source=1.8 12 | sonar.java.binaries=target/classes 13 | 14 | # Language 15 | sonar.language=java 16 | 17 | # Encoding of the source files 18 | sonar.sourceEncoding=UTF-8 19 | -------------------------------------------------------------------------------- /src/main/java/weblogic/logging/exporter/LogExportHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.logging.Handler; 10 | import java.util.logging.Level; 11 | import java.util.logging.LogRecord; 12 | import javax.ws.rs.client.Client; 13 | import javax.ws.rs.client.ClientBuilder; 14 | import javax.ws.rs.client.Entity; 15 | import javax.ws.rs.client.Invocation; 16 | import javax.ws.rs.client.WebTarget; 17 | import javax.ws.rs.core.Response; 18 | import weblogic.diagnostics.logging.LogVariablesImpl; 19 | import weblogic.diagnostics.query.QueryException; 20 | import weblogic.i18n.logging.Severities; 21 | import weblogic.logging.WLLevel; 22 | import weblogic.logging.WLLogRecord; 23 | import weblogic.logging.exporter.config.Config; 24 | import weblogic.logging.exporter.config.FilterConfig; 25 | 26 | @SuppressWarnings("UnnecessaryContinue") 27 | class LogExportHandler extends Handler { 28 | 29 | private static final String DOC_TYPE = "doc"; 30 | private static final String INDEX = " { \"index\" : { }} "; 31 | private static final int offValue = Level.OFF.intValue(); 32 | 33 | private final Client httpClient = ClientBuilder.newClient(); 34 | private List filterConfigs = new ArrayList<>(); 35 | private final List payloadBulkList = new ArrayList<>(); 36 | 37 | // 38 | // These will all be set by initialize() 39 | // 40 | private String indexName; 41 | private String publishHost; 42 | private int publishPort; 43 | private int bulkSize; 44 | private String httpHostPort; 45 | private String singleURL; 46 | private String bulkURL; 47 | private String fluentdURL; 48 | private String domainUID; 49 | private String destination; 50 | 51 | public LogExportHandler(Config config) { 52 | initialize(config); 53 | createMappings(); 54 | } 55 | 56 | @Override 57 | public void publish(LogRecord record) { 58 | WLLogRecord wlLogRecord = (WLLogRecord) record; 59 | if (!isLoggable(record)) { 60 | return; 61 | } 62 | String payload = recordToPayload(wlLogRecord); 63 | if (bulkSize <= 1) { 64 | Result result = executePutOrPostOnUrl(singleURL, payload, true); 65 | if (!result.successful) { 66 | System.out.println( 67 | " logging of " 68 | + payload 69 | + " got result " 70 | + result); 71 | } 72 | } else { 73 | payloadBulkList.add(payload); 74 | if (payloadBulkList.size() >= bulkSize) { 75 | writeOutAllRecords(); 76 | } 77 | } 78 | } 79 | 80 | @Override 81 | public void flush() { 82 | writeOutAllRecords(); 83 | } 84 | 85 | @Override 86 | public void close() throws SecurityException {} 87 | 88 | @Override 89 | public boolean isLoggable(LogRecord logEntry) { 90 | final int levelValue = getLevel().intValue(); 91 | if (logEntry.getLevel().intValue() < levelValue || levelValue == offValue) { 92 | return false; 93 | } 94 | for (FilterConfig oneConfig : filterConfigs) { 95 | 96 | List servers = oneConfig.getServers(); 97 | if (servers.size() == 0) { 98 | if (oneConfig.getQuery() != null) { 99 | if (applyFilter(oneConfig, (WLLogRecord) logEntry, null)) { 100 | continue; 101 | } else { 102 | return false; 103 | } 104 | } 105 | } else { 106 | for (String server : servers) { 107 | if (applyFilter(oneConfig, (WLLogRecord) logEntry, server)) { 108 | continue; 109 | } else { 110 | return false; 111 | } 112 | } 113 | } 114 | } 115 | return true; 116 | } 117 | 118 | private boolean applyFilter(FilterConfig oneConfig, WLLogRecord logEntry, String serverName) { 119 | try { 120 | if ((serverName == null) || (serverName.equals(logEntry.getServerName()))) { 121 | return oneConfig 122 | .getQuery() 123 | .executeQuery(LogVariablesImpl.getInstance().getLogVariablesResolver(logEntry)); 124 | } else { 125 | return true; 126 | } 127 | } catch (QueryException ex) { 128 | // if there is any error with this expression. 129 | // TODO: give warning ? 130 | return true; 131 | } 132 | } 133 | 134 | private String dataAsJson(String fieldName, String data) { 135 | return "\"" + fieldName + "\": \"" + data.replace("\"", "\\\"") + "\""; 136 | } 137 | 138 | private String dataAsJson(String fieldName, long data) { 139 | return "\"" + fieldName + "\": " + data; 140 | } 141 | 142 | private void writeOutAllRecords() { 143 | StringBuilder buffer = new StringBuilder(); 144 | for (String oneRecord : payloadBulkList) { 145 | buffer.append(INDEX); 146 | buffer.append("\n"); 147 | buffer.append(oneRecord); 148 | buffer.append("\n"); 149 | } 150 | payloadBulkList.clear(); 151 | Result result = executePutOrPostOnUrl(bulkURL, buffer.toString(), true); 152 | if (!result.successful) { 153 | System.out.println( 154 | " logging of " 155 | + buffer.toString() 156 | + " got result " 157 | + result); 158 | } 159 | } 160 | 161 | private Result executePutOrPostOnUrl(String url, String payload, boolean post) { 162 | WebTarget target = httpClient.target(url); 163 | Invocation.Builder invocationBuilder = target.request().accept("application/json"); 164 | Response response = 165 | post 166 | ? invocationBuilder.post(Entity.json(payload)) 167 | : invocationBuilder.put(Entity.json(payload)); 168 | String responseString = null; 169 | int status = response.getStatus(); 170 | boolean successful = false; 171 | if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) { 172 | successful = true; 173 | if (response.hasEntity()) { 174 | responseString = String.valueOf(response.readEntity(String.class)); 175 | } 176 | } 177 | return new Result(responseString, status, successful); 178 | } 179 | 180 | private String recordToPayload(WLLogRecord wlLogRecord) { 181 | return "{" 182 | + dataAsJson("messageID", wlLogRecord.getId()) 183 | + "," 184 | + dataAsJson("message", wlLogRecord.getMessage()) 185 | + "," 186 | + dataAsJson("timestamp", wlLogRecord.getMillis()) 187 | + "," 188 | + dataAsJson("serverName", wlLogRecord.getServerName()) 189 | + "," 190 | + dataAsJson("threadName", wlLogRecord.getThreadName()) 191 | + "," 192 | + dataAsJson("severity", wlLogRecord.getSeverityString()) 193 | + "," 194 | + dataAsJson("userId", wlLogRecord.getUserId()) 195 | + "," 196 | + dataAsJson("level", wlLogRecord.getLevel().toString()) 197 | + "," 198 | + dataAsJson("loggerName", wlLogRecord.getLoggerName()) 199 | + "," 200 | + dataAsJson("formattedDate", wlLogRecord.getFormattedDate()) 201 | + "," 202 | + dataAsJson("subSystem", wlLogRecord.getSubsystem()) 203 | + "," 204 | + dataAsJson("machineName", wlLogRecord.getMachineName()) 205 | + "," 206 | + dataAsJson("transactionId", wlLogRecord.getTransactionId()) 207 | + "," 208 | + dataAsJson("diagnosticContextId", wlLogRecord.getDiagnosticContextId()) 209 | + "," 210 | + dataAsJson("sequenceNumber", wlLogRecord.getSequenceNumber()) 211 | + "," 212 | + dataAsJson("domainUID", domainUID) 213 | + "}"; 214 | } 215 | 216 | private void initialize(Config config) { 217 | 218 | publishHost = config.getHost(); 219 | publishPort = config.getPort(); 220 | @SuppressWarnings("unused") 221 | boolean enabled = config.getEnabled(); 222 | String severity = config.getSeverity(); 223 | if (severity != null) { 224 | setLevel(WLLevel.getLevel(Severities.severityStringToNum(severity))); 225 | } 226 | indexName = config.getIndexName(); 227 | bulkSize = config.getBulkSize(); 228 | filterConfigs = config.getFilterConfigs(); 229 | httpHostPort = "http://" + publishHost + ":" + publishPort; 230 | singleURL = httpHostPort + "/" + indexName + "/" + DOC_TYPE + "/?pretty"; 231 | bulkURL = httpHostPort + "/" + indexName + "/" + DOC_TYPE + "/_bulk?pretty"; 232 | domainUID = config.getDomainUID(); 233 | 234 | // 235 | // Set up the publishing variables... 236 | // 237 | 238 | httpHostPort = "http://" + publishHost + ":" + publishPort; 239 | singleURL = httpHostPort + "/" + indexName + "/" + DOC_TYPE + "/?pretty"; 240 | bulkURL = httpHostPort + "/" + indexName + "/" + DOC_TYPE + "/_bulk?pretty"; 241 | fluentdURL = httpHostPort + "/" + indexName; 242 | } 243 | 244 | private void createMappings() { 245 | // create mapping for wls elasticsearch document 246 | final String mappings = 247 | "{" 248 | + " \"mappings\": {" 249 | + " \"" 250 | + DOC_TYPE 251 | + "\": {" 252 | + " \"properties\": {" 253 | + " \"timestamp\": {" 254 | + "\"type\": \"date\" " 255 | + "}," 256 | + " \"sequenceNumber\": {" 257 | + "\"type\": \"keyword\" " 258 | + "}," 259 | + " \"severity\": {" 260 | + "\"type\": \"keyword\" " 261 | + "}," 262 | + " \"level\": {" 263 | + "\"type\": \"keyword\" " 264 | + "}," 265 | + " \"serverName\": {" 266 | + "\"type\": \"keyword\" " 267 | + "}," 268 | + " \"threadName\": {" 269 | + "\"type\": \"keyword\" " 270 | + "}," 271 | + " \"userId\": {" 272 | + "\"type\": \"keyword\" " 273 | + "}," 274 | + " \"loggerName\": {" 275 | + "\"type\": \"keyword\" " 276 | + "}," 277 | + " \"subSystem\": {" 278 | + "\"type\": \"keyword\" " 279 | + "}," 280 | + " \"machineName\": {" 281 | + "\"type\": \"keyword\" " 282 | + "}," 283 | + " \"transactionId\": {" 284 | + "\"type\": \"keyword\" " 285 | + "}," 286 | + " \"messageID\": {" 287 | + "\"type\": \"keyword\" " 288 | + "}," 289 | + " \"domainUID\": {" 290 | + "\"type\": \"keyword\" " 291 | + "}" 292 | + " }" 293 | + " }" 294 | + " }" 295 | + "}"; 296 | 297 | Result result = executePutOrPostOnUrl(httpHostPort + "/" + indexName, mappings, false); 298 | if (!result.successful) { 299 | //noinspection StatementWithEmptyBody 300 | if (result.getStatus() == HttpURLConnection.HTTP_BAD_REQUEST) { 301 | // ignore. this is the case where the index has been created in elastic search. 302 | } else { 303 | System.out.println( 304 | " issue of " 305 | + mappings 306 | + " got result " 307 | + result); 308 | } 309 | } 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /src/main/java/weblogic/logging/exporter/Result.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter; 5 | 6 | /** 7 | * Holder of response received from REST requests invoked using methods in {@link LogExportHandler} 8 | * class 9 | */ 10 | @SuppressWarnings("unused") 11 | class Result { 12 | 13 | private final String response; 14 | private final int status; 15 | final boolean successful; 16 | 17 | public Result(String response, int status, boolean successful) { 18 | this.response = response; 19 | this.status = status; 20 | this.successful = successful; 21 | } 22 | 23 | /** @return The String response received from the REST request */ 24 | public String getResponse() { 25 | return response; 26 | } 27 | 28 | /** @return HTTP status code from the REST request */ 29 | public int getStatus() { 30 | return status; 31 | } 32 | 33 | /** 34 | * @return True if the REST request returns a status code that indicates successful request, false 35 | * otherwise 36 | */ 37 | public boolean isSuccessful() { 38 | return successful; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "Result{" 44 | + "response='" 45 | + response 46 | + '\'' 47 | + ", status=" 48 | + status 49 | + ", successful=" 50 | + successful 51 | + '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/weblogic/logging/exporter/Startup.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter; 5 | 6 | import java.io.File; 7 | import java.util.logging.FileHandler; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | import weblogic.logging.LoggingHelper; 12 | import weblogic.logging.ServerLoggingHandler; 13 | import weblogic.logging.exporter.config.Config; 14 | 15 | public class Startup { 16 | 17 | private static final String DEFAULT_CONFIG_FILE = "config/WebLogicLoggingExporter.yaml"; 18 | 19 | public static void main(String[] argv) { 20 | System.out.println("======================= WebLogic Logging Exporter Startup class called"); 21 | try { 22 | Logger logger = LoggingHelper.getServerLogger(); 23 | 24 | /* 25 | We will read from the system variable for the location and name of the configuration file. 26 | If the environment variable is not set, will use the default file under Domain Home Config directory. 27 | If the file doesn't exist, give WARNING, and use default. 28 | It is assumed that when this is integrated to Operator, the system variable will be set. 29 | */ 30 | 31 | String fileName = 32 | System.getProperty( 33 | "WEBLOGIC_LOGGING_EXPORTER_CONFIG_FILE", 34 | System.getenv("WEBLOGIC_LOGGING_EXPORTER_CONFIG_FILE")); 35 | System.out.println( 36 | "JavaProperty/EnvVariable WEBLOGIC_LOGGING_EXPORTER_CONFIG_FILE:" + fileName); 37 | if (fileName == null || fileName.isEmpty()) { 38 | System.out.println( 39 | "Env variable WEBLOGIC_LOGGING_EXPORTER_CONFIG_FILE is not set. Defaulting to:" 40 | + DEFAULT_CONFIG_FILE); 41 | fileName = DEFAULT_CONFIG_FILE; 42 | } 43 | File file = new File(fileName); 44 | System.out.println("Reading configuration from file name: " + file.getAbsolutePath()); 45 | Config config = Config.loadConfig(file); 46 | System.out.println(config); 47 | 48 | // Elastic log handler is enabled or the file log handler 49 | if (config.getEnabled()) { 50 | logger.addHandler(new LogExportHandler(config)); 51 | } else if (config.isFileLoggingEnabled()) { 52 | // Because of this bridge log messages in the applications themselves are being forwarded to the server logger. 53 | // so that logging in ear/war artifacts are also visible to the server logger and appear in the JSON log file. 54 | Logger.getLogger("").addHandler(new ServerLoggingHandler()); 55 | 56 | // Register a file handler using the provided config 57 | FileHandler fh = new FileHandler(config.getOutputFile(), config.getMaxFileSize(), config.getGetMaxRollbackFiles(), config.getAppendToFile()); 58 | fh.setLevel(Level.parse(config.getFileLoggingLogLevel())); 59 | fh.setFormatter(new WebLogicLogFormatter(config.getDomainUID())); 60 | logger.addHandler(fh); 61 | } else { 62 | System.out.println("WebLogic Elasticsearch Logging Exporter is disabled"); 63 | } 64 | // also catch errors so that WebLogic does not crash when a required library was not placed in the classpath correctly. 65 | } catch (Error | Exception e) { 66 | System.out.println("======================= Something went wrong, the WebLogic Logging Exporter is not activated"); 67 | e.printStackTrace(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/weblogic/logging/exporter/WebLogicLogFormatter.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter; 5 | 6 | import co.elastic.logging.jul.EcsFormatter; 7 | import org.slf4j.MDC; 8 | import weblogic.logging.WLLogRecord; 9 | 10 | import java.util.logging.LogRecord; 11 | 12 | public class WebLogicLogFormatter extends EcsFormatter { 13 | public static final String FIELDS_MESSAGE_ID = "fields.messageID"; 14 | public static final String FIELDS_SERVER_NAME = "fields.serverName"; 15 | public static final String FIELDS_USER_ID = "fields.userId"; 16 | public static final String FIELDS_SUB_SYSTEM = "fields.subSystem"; 17 | public static final String FIELDS_MACHINE_NAME = "fields.machineName"; 18 | public static final String FIELDS_TRANSACTION_ID = "fields.transactionId"; 19 | public static final String FIELDS_DIAGNOSTIC_CONTEXT_ID = "fields.diagnosticContextId"; 20 | public static final String FIELDS_SEQUENCE_NUMBER = "fields.sequenceNumber"; 21 | public static final String FIELDS_DOMAIN_UID = "fields.domainUID"; 22 | 23 | private final String domainUID; 24 | 25 | public WebLogicLogFormatter(String domainUID) { 26 | this.domainUID = domainUID; 27 | } 28 | 29 | @Override 30 | public String format(final LogRecord record) { 31 | WLLogRecord wlLogRecord = (WLLogRecord) record; 32 | 33 | MDC.put(FIELDS_MESSAGE_ID, wlLogRecord.getId()); 34 | MDC.put(FIELDS_SERVER_NAME, wlLogRecord.getServerName()); 35 | MDC.put(FIELDS_USER_ID, wlLogRecord.getUserId()); 36 | MDC.put(FIELDS_SUB_SYSTEM, wlLogRecord.getSubsystem()); 37 | MDC.put(FIELDS_MACHINE_NAME, wlLogRecord.getMachineName()); 38 | MDC.put(FIELDS_TRANSACTION_ID, wlLogRecord.getTransactionId()); 39 | MDC.put(FIELDS_DIAGNOSTIC_CONTEXT_ID, wlLogRecord.getDiagnosticContextId()); 40 | MDC.put(FIELDS_SEQUENCE_NUMBER, String.valueOf(wlLogRecord.getSequenceNumber())); 41 | MDC.put(FIELDS_DOMAIN_UID, domainUID); 42 | 43 | String result = super.format(wlLogRecord); 44 | 45 | // Can't clear the whole MDC HashMap as there might be other records in there. 46 | MDC.remove(FIELDS_MESSAGE_ID); 47 | MDC.remove(FIELDS_SERVER_NAME); 48 | MDC.remove(FIELDS_USER_ID); 49 | MDC.remove(FIELDS_SUB_SYSTEM); 50 | MDC.remove(FIELDS_MACHINE_NAME); 51 | MDC.remove(FIELDS_TRANSACTION_ID); 52 | MDC.remove(FIELDS_DIAGNOSTIC_CONTEXT_ID); 53 | MDC.remove(FIELDS_SEQUENCE_NUMBER); 54 | MDC.remove(FIELDS_DOMAIN_UID); 55 | 56 | return result; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/weblogic/logging/exporter/config/Config.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter.config; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileNotFoundException; 9 | import java.io.InputStream; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | import org.yaml.snakeyaml.Yaml; 15 | import org.yaml.snakeyaml.scanner.ScannerException; 16 | 17 | public class Config { 18 | 19 | public static final String DEFAULT_HOST = "localhost"; 20 | public static final int DEFAULT_PORT = 9200; 21 | public static final String DEFAULT_INDEX_NAME = "wls"; 22 | public static final int DEFAULT_BULK_SIZE = 1; 23 | public static final String DEFAULT_DOMAIN_UID = "unknown"; 24 | 25 | private static final String HOST = "publishHost"; 26 | private static final String PORT = "publishPort"; 27 | private static final String FILTERS = "weblogicLoggingExporterFilters"; 28 | private static final String ENABLED = "weblogicLoggingExporterEnabled"; 29 | private static final String SEVERITY = "weblogicLoggingExporterSeverity"; 30 | private static final String BULK_SIZE = "weblogicLoggingExporterBulkSize"; 31 | private static final String INDEX_NAME = "weblogicLoggingIndexName"; 32 | private static final String DOMAIN_UID = "domainUID"; 33 | 34 | private static final String WRITE_TO_FILE_ENABLED = "writeToFileEnabled"; 35 | private static final String OUTPUT_FILE = "outputFile"; 36 | private static final String MAX_ROLLBACK_FILES = "maxRollbackFiles"; 37 | private static final String MAX_FILE_SIZE = "maxFileSize"; 38 | private static final String APPEND_TO_FILE = "appendToFile"; 39 | private static final String FILE_LOGGING_LOG_LEVEL = "fileLoggingLogLevel"; 40 | 41 | private String host = DEFAULT_HOST; 42 | private int port = DEFAULT_PORT; 43 | private String indexName = DEFAULT_INDEX_NAME; 44 | private int bulkSize = DEFAULT_BULK_SIZE; 45 | private boolean enabled = true; 46 | private String severity = null; 47 | private final List filterConfigs = new ArrayList<>(); 48 | private String domainUID = DEFAULT_DOMAIN_UID; 49 | 50 | private boolean fileLoggingEnabled; 51 | private String outputFile; 52 | private Integer getMaxRollbackFiles; 53 | private Integer maxFileSize; 54 | private boolean appendToFile; 55 | 56 | private String fileLoggingLogLevel = "INFO"; 57 | 58 | private Config() {} 59 | 60 | private Config(Map yaml) { 61 | if (yaml.containsKey(HOST)) { 62 | host = MapUtils.getStringValue(yaml, HOST); 63 | } 64 | if (yaml.containsKey(PORT)) { 65 | port = MapUtils.getIntegerValue(yaml, PORT); 66 | } 67 | if (yaml.containsKey(ENABLED)) { 68 | enabled = MapUtils.getBooleanValue(yaml, ENABLED); 69 | } 70 | if (yaml.containsKey(SEVERITY)) { 71 | severity = MapUtils.getStringValue(yaml, SEVERITY); 72 | } 73 | if (yaml.containsKey(BULK_SIZE)) { 74 | bulkSize = MapUtils.getIntegerValue(yaml, BULK_SIZE); 75 | } 76 | if (yaml.containsKey(INDEX_NAME)) { 77 | indexName = MapUtils.getStringValue(yaml, INDEX_NAME); 78 | } 79 | if (yaml.containsKey(DOMAIN_UID)) { 80 | domainUID = MapUtils.getStringValue(yaml, DOMAIN_UID); 81 | } 82 | if (bulkSize <= 1) { 83 | bulkSize = 1; 84 | } 85 | // index name needs to be all lowercase. 86 | if (yaml.containsKey(INDEX_NAME)) { 87 | indexName = MapUtils.getStringValue(yaml, INDEX_NAME); 88 | } 89 | if (!(indexName.toLowerCase().equals(indexName))) { 90 | indexName = indexName.toLowerCase(); 91 | System.out.println("Index name is converted to all lower case : " + indexName); 92 | } 93 | if (yaml.containsKey(FILTERS)) appendFilters(yaml.get(FILTERS)); 94 | 95 | // File output 96 | if (yaml.containsKey(WRITE_TO_FILE_ENABLED)) { 97 | fileLoggingEnabled = MapUtils.getBooleanValue(yaml, WRITE_TO_FILE_ENABLED); 98 | } 99 | if (yaml.containsKey(OUTPUT_FILE)) { 100 | outputFile = MapUtils.getStringValue(yaml, OUTPUT_FILE); 101 | } 102 | if (yaml.containsKey(MAX_ROLLBACK_FILES)) { 103 | getMaxRollbackFiles = MapUtils.getIntegerValue(yaml, MAX_ROLLBACK_FILES); 104 | } 105 | if (yaml.containsKey(MAX_FILE_SIZE)) { 106 | maxFileSize = MapUtils.getIntegerValue(yaml, MAX_FILE_SIZE); 107 | } 108 | if (yaml.containsKey(APPEND_TO_FILE)) { 109 | appendToFile = MapUtils.getBooleanValue(yaml, APPEND_TO_FILE); 110 | } 111 | if (yaml.containsKey(FILE_LOGGING_LOG_LEVEL)) { 112 | fileLoggingLogLevel = MapUtils.getStringValue(yaml, FILE_LOGGING_LOG_LEVEL); 113 | } 114 | } 115 | 116 | public static Config loadConfig(File file) { 117 | try { 118 | return loadConfig(new FileInputStream(file)); 119 | } catch (FileNotFoundException e) { 120 | System.out.println(file.toString() + " Not Found"); 121 | } catch (YamlParserException ex) { 122 | System.out.println("Error parsing configuration file : " + file.toString()); 123 | } 124 | System.out.println("Using default for all parameters"); 125 | return new Config(); 126 | } 127 | 128 | private void appendFilters(Object filtersYaml) { 129 | for (Map filterYaml : getAsListOfMaps(filtersYaml)) { 130 | filterConfigs.add(FilterConfig.create(filterYaml)); 131 | } 132 | } 133 | 134 | /** 135 | * Loads a YAML configuration to create a new configuration object. 136 | * 137 | * @param inputStream a reader of a YAML configuration. 138 | * @return an ExporterConfig object that matches the parsed YAML 139 | */ 140 | private static Config loadConfig(InputStream inputStream) { 141 | try { 142 | return loadConfig(asMap(new Yaml().load(inputStream))); 143 | } catch (ScannerException e) { 144 | throw new YamlParserException(e); 145 | } 146 | } 147 | 148 | @SuppressWarnings("unchecked") 149 | private static Map asMap(Object yaml) { 150 | try { 151 | return (Map) yaml; 152 | } catch (ClassCastException e) { 153 | throw new ConfigurationException(ConfigurationException.NOT_YAML_FORMAT); 154 | } 155 | } 156 | 157 | @SuppressWarnings("unchecked") 158 | private List> getAsListOfMaps(Object queriesYaml) { 159 | if (!isArrayOfMaps(queriesYaml)) 160 | throw MapUtils.createBadTypeException(FILTERS, queriesYaml, "a list of structures"); 161 | 162 | return (List>) queriesYaml; 163 | } 164 | 165 | private boolean isArrayOfMaps(Object object) { 166 | return List.class.isAssignableFrom(object.getClass()) && emptyOrContainsMaps((List) object); 167 | } 168 | 169 | private boolean emptyOrContainsMaps(List list) { 170 | return list.isEmpty() || list.get(0) instanceof Map; 171 | } 172 | 173 | private static Config loadConfig(Map yamlConfig) { 174 | if (yamlConfig == null) yamlConfig = new HashMap<>(); 175 | 176 | return new Config(yamlConfig); 177 | } 178 | 179 | @Override 180 | public String toString() { 181 | return "Config{" + 182 | "host='" + host + '\'' + 183 | ", port=" + port + 184 | ", indexName='" + indexName + '\'' + 185 | ", bulkSize=" + bulkSize + 186 | ", enabled=" + enabled + 187 | ", severity='" + severity + '\'' + 188 | ", filterConfigs=" + filterConfigs + 189 | ", domainUID='" + domainUID + '\'' + 190 | ", fileLoggingEnabled=" + fileLoggingEnabled + 191 | ", outputFile='" + outputFile + '\'' + 192 | ", getMaxRollbackFiles=" + getMaxRollbackFiles + 193 | ", maxFileSize=" + maxFileSize + 194 | ", appendToFile=" + appendToFile + 195 | ", fileLoggingLogLevel='" + fileLoggingLogLevel + '\'' + 196 | '}'; 197 | } 198 | 199 | public String getHost() { 200 | return host; 201 | } 202 | 203 | public int getPort() { 204 | return port; 205 | } 206 | 207 | public boolean getEnabled() { 208 | return enabled; 209 | } 210 | 211 | public String getIndexName() { 212 | return indexName; 213 | } 214 | 215 | public String getSeverity() { 216 | return severity; 217 | } 218 | 219 | public List getFilterConfigs() { 220 | return filterConfigs; 221 | } 222 | 223 | public int getBulkSize() { 224 | return bulkSize; 225 | } 226 | 227 | public String getDomainUID() { 228 | return domainUID; 229 | } 230 | 231 | public boolean isFileLoggingEnabled() { 232 | return fileLoggingEnabled; 233 | } 234 | 235 | public String getOutputFile() { 236 | return outputFile; 237 | } 238 | 239 | public Integer getGetMaxRollbackFiles() { 240 | return getMaxRollbackFiles; 241 | } 242 | 243 | public Integer getMaxFileSize() { 244 | return maxFileSize; 245 | } 246 | 247 | public boolean getAppendToFile() { 248 | return appendToFile; 249 | } 250 | 251 | public String getFileLoggingLogLevel() { 252 | return fileLoggingLogLevel; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/main/java/weblogic/logging/exporter/config/ConfigurationException.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter.config; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | class ConfigurationException extends RuntimeException { 10 | static final String BAD_YAML_FORMAT = "Configuration YAML format has errors"; 11 | public static final String NOT_YAML_FORMAT = "Configuration is not in YAML format"; 12 | 13 | private final List context = new ArrayList<>(); 14 | 15 | ConfigurationException(String description) { 16 | super(description); 17 | } 18 | 19 | @SuppressWarnings("unused") 20 | void addContext(String parentContext) { 21 | context.add(0, parentContext); 22 | } 23 | 24 | @Override 25 | public String getMessage() { 26 | StringBuilder sb = new StringBuilder(super.getMessage()); 27 | if (!context.isEmpty()) sb.append(" at ").append(String.join(".", context)); 28 | return sb.toString(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/weblogic/logging/exporter/config/FilterConfig.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter.config; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Set; 12 | import weblogic.diagnostics.logging.LogVariablesImpl; 13 | import weblogic.diagnostics.query.Query; 14 | import weblogic.diagnostics.query.QueryException; 15 | import weblogic.diagnostics.query.QueryFactory; 16 | 17 | public class FilterConfig { 18 | private static final String EXPRESSION = "FilterExpression"; 19 | private static final String SERVERS = "FilterServers"; 20 | 21 | private String expression; 22 | private String[] servers = new String[0]; 23 | private Query query = null; 24 | 25 | private FilterConfig(Map map) { 26 | for (String key : map.keySet()) { 27 | switch (key) { 28 | case EXPRESSION: 29 | expression = MapUtils.getStringValue(map, EXPRESSION); 30 | try { 31 | LogVariablesImpl lv = LogVariablesImpl.getInstance(); 32 | query = QueryFactory.createQuery(lv, lv, expression); 33 | } catch (QueryException ex) { 34 | System.out.println("Error Parsing expression: " + expression); 35 | } 36 | break; 37 | case SERVERS: 38 | setServers(MapUtils.getStringArray(map, SERVERS)); 39 | break; 40 | default: 41 | break; 42 | } 43 | } 44 | } 45 | 46 | static FilterConfig create(Map map) { 47 | return new FilterConfig(map); 48 | } 49 | 50 | private void setServers(String[] values) { 51 | if (values.length == 0) throw new ConfigurationException("Values specified as empty array"); 52 | 53 | Set uniqueValues = new HashSet<>(Arrays.asList(values)); 54 | if (values.length != uniqueValues.size()) reportDuplicateValues(values, uniqueValues); 55 | this.servers = values; 56 | } 57 | 58 | private void reportDuplicateValues(String[] values, Set uniqueValues) { 59 | ArrayList duplicate = new ArrayList<>(Arrays.asList(values)); 60 | for (String unique : uniqueValues) duplicate.remove(unique); 61 | 62 | throw new ConfigurationException("Duplicate values for " + duplicate); 63 | } 64 | 65 | public Query getQuery() { 66 | return query; 67 | } 68 | 69 | public List getServers() { 70 | return Arrays.asList(servers); 71 | } 72 | 73 | @Override 74 | public String toString() { 75 | return "FilterConfig{" 76 | + "expression='" 77 | + expression 78 | + '\'' 79 | + ", servers=" 80 | + Arrays.toString(servers) 81 | + '}'; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/weblogic/logging/exporter/config/MapUtils.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter.config; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | @SuppressWarnings("SameParameterValue") 10 | class MapUtils { 11 | 12 | private static final String ILLEGAL_VALUE_FORMAT = "Illegal value for %s: %s. Value must be %s"; 13 | 14 | /** 15 | * Returns the specified map value as a string. 16 | * 17 | * @param map a map containing the value 18 | * @param key the map key at which the value is found 19 | * @return the string representation of the value 20 | */ 21 | static String getStringValue(Map map, String key) { 22 | return map.get(key).toString(); 23 | } 24 | 25 | /** 26 | * Attempts to retrieve the specified value as an integer. It can recognize the value either as a 27 | * Number object or a string to be parsed. 28 | * 29 | * @param map a map containing the value 30 | * @param key the map key at which the value is found 31 | * @return an integer value derived from the item in the map 32 | */ 33 | static Integer getIntegerValue(Map map, String key) { 34 | Object value = map.get(key); 35 | if (value instanceof Number) return ((Number) value).intValue(); 36 | 37 | try { 38 | return Integer.parseInt(value.toString()); 39 | } catch (NumberFormatException e) { 40 | String expectedType = "an integer"; 41 | throw createBadTypeException(key, value, expectedType); 42 | } 43 | } 44 | 45 | /** 46 | * Returns the specified map value as an array of strings. If it is a scalar, it will be returned 47 | * as an array containing only the found value. 48 | * 49 | * @param map a map containing the value 50 | * @param key the map key at which the value is found 51 | * @return the string representation of the value 52 | */ 53 | @SuppressWarnings("unchecked") 54 | static String[] getStringArray(Map map, String key) { 55 | Object value = map.get(key); 56 | if (value instanceof List) { 57 | return toStringArray((List) value); 58 | } else if (!value.getClass().isArray()) { 59 | return new String[] {value.toString()}; 60 | } else if (value.getClass().getComponentType() == String.class) { 61 | return (String[]) value; 62 | } else { 63 | throw createBadTypeException(key, value, "an array of strings"); 64 | } 65 | } 66 | 67 | private static String[] toStringArray(List list) { 68 | String[] result = new String[list.size()]; 69 | for (int i = 0; i < result.length; i++) { 70 | result[i] = list.get(i).toString(); 71 | } 72 | return result; 73 | } 74 | 75 | /** 76 | * Attempts to retrieve the specified value as a boolean. It can recognize the value either as a 77 | * Boolean object or a string. 78 | * 79 | * @param map a map containing the value 80 | * @param key the map key at which the value is found 81 | * @return a boolean value derived from the item in the map 82 | */ 83 | static Boolean getBooleanValue(Map map, String key) { 84 | Object value = map.get(key); 85 | if (value instanceof Boolean) { 86 | return (Boolean) value; 87 | } 88 | 89 | if (inValues(value, TRUE_VALUES)) { 90 | return true; 91 | } 92 | if (inValues(value, FALSE_VALUES)) { 93 | return false; 94 | } 95 | throw new ConfigurationException("Unable to interpret '" + value + "' as a boolean value"); 96 | } 97 | 98 | private static final String[] TRUE_VALUES = {"true", "t", "yes", "on", "y"}; 99 | private static final String[] FALSE_VALUES = {"false", "f", "no", "off", "n"}; 100 | 101 | private static boolean inValues(Object candidate, String... matches) { 102 | for (String match : matches) { 103 | if (candidate.toString().equalsIgnoreCase(match)) { 104 | return true; 105 | } 106 | } 107 | return false; 108 | } 109 | 110 | /** 111 | * Creates an exception which describes the failure to interpret a map value 112 | * 113 | * @param key the map key used to retrieve the value 114 | * @param value the actual problematic value found 115 | * @param expectedType a description of the type permitted 116 | * @return an exception which can be thrown to report a problem 117 | */ 118 | static IllegalArgumentException createBadTypeException( 119 | String key, Object value, String expectedType) { 120 | return new IllegalArgumentException( 121 | String.format(ILLEGAL_VALUE_FORMAT, key, value, expectedType)); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/weblogic/logging/exporter/config/YamlParserException.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter.config; 5 | 6 | import org.yaml.snakeyaml.scanner.ScannerException; 7 | 8 | /** An exception thrown when there is an error parsing the YAML. */ 9 | public class YamlParserException extends ConfigurationException { 10 | private final ScannerException scannerException; 11 | 12 | YamlParserException(ScannerException scannerException) { 13 | super(BAD_YAML_FORMAT); 14 | this.scannerException = scannerException; 15 | } 16 | 17 | @Override 18 | public String getMessage() { 19 | return super.getMessage() + '\n' + scannerException.getMessage(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/test/java/weblogic/logging/exporter/ResultTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter; 5 | 6 | import static org.hamcrest.CoreMatchers.containsString; 7 | import static org.hamcrest.MatcherAssert.assertThat; 8 | import static org.hamcrest.core.AllOf.allOf; 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | import org.junit.jupiter.api.DisplayName; 12 | import org.junit.jupiter.api.Test; 13 | 14 | @DisplayName("Test the Result object") 15 | public class ResultTest { 16 | 17 | // using 200 for status field just to test it is set/got correctly 18 | // 200 is HTTP status for OK 19 | private static final String EXPECTED_STRING = 20 | "Result{response='good', status=200, successful=true}"; 21 | 22 | @DisplayName("After creating a Result, make sure I can retrieve the field values") 23 | @Test 24 | public void afterCreated_canRetrieveFieldValues() { 25 | Result result = new Result("good", 200, true); 26 | 27 | assertEquals("good", result.getResponse()); 28 | assertEquals(200, result.getStatus()); 29 | assertEquals(true, result.isSuccessful()); 30 | } 31 | 32 | @DisplayName("Check toString() includes response, status and successful") 33 | @Test 34 | public void toStringIncludesResponseStatusAndSuccessful() { 35 | Result result = new Result("good", 1, true); 36 | assertThat( 37 | result.toString(), 38 | allOf(containsString("good"), containsString("1"), containsString("true"))); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/weblogic/logging/exporter/config/ConfigTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter.config; 5 | 6 | import static org.junit.jupiter.api.Assertions.*; 7 | 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.File; 10 | import java.io.PrintStream; 11 | import java.util.ArrayList; 12 | import org.junit.jupiter.api.AfterEach; 13 | import org.junit.jupiter.api.BeforeEach; 14 | import org.junit.jupiter.api.DisplayName; 15 | import org.junit.jupiter.api.Test; 16 | 17 | @DisplayName("Test the Config class") 18 | public class ConfigTest { 19 | 20 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 21 | private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); 22 | private final PrintStream originalOut = System.out; 23 | private final PrintStream originalErr = System.err; 24 | 25 | private static final String EXPECTED_STRING = "Config{host='host1', port=1234, indexName='index1', bulkSize=2, enabled=false, severity='Warning', filterConfigs=[FilterConfig{expression='MSGID != 'BEA-000449'', servers=[]}], domainUID='domain1', fileLoggingEnabled=false, outputFile='null', getMaxRollbackFiles=null, maxFileSize=null, appendToFile=false, fileLoggingLogLevel='INFO'}"; 26 | 27 | @BeforeEach 28 | public void setUpStreams() { 29 | System.setOut(new PrintStream(outContent)); 30 | System.setErr(new PrintStream(errContent)); 31 | } 32 | 33 | @AfterEach 34 | public void restoreStreams() { 35 | System.setOut(originalOut); 36 | System.setErr(originalErr); 37 | } 38 | 39 | @DisplayName("Create the default config from an empty file") 40 | @Test 41 | public void createDefaultConfigFromEmptyFile() { 42 | // create config by loading an empty file 43 | Config config = Config.loadConfig(new File("src/test/resources/emptyConfig.yaml")); 44 | 45 | // now check that the config contains the expected default values 46 | assertAll( 47 | "config", 48 | () -> assertEquals("localhost", config.getHost()), 49 | () -> assertEquals(9200, config.getPort()), 50 | () -> assertEquals(true, config.getEnabled()), 51 | () -> assertEquals("wls", config.getIndexName()), 52 | () -> assertEquals(null, config.getSeverity()), 53 | () -> assertTrue(config.getFilterConfigs() instanceof ArrayList), 54 | () -> assertEquals(0, config.getFilterConfigs().size()), 55 | () -> assertEquals(1, config.getBulkSize()), 56 | () -> assertEquals("unknown", config.getDomainUID())); 57 | } 58 | 59 | @DisplayName("Create config from file") 60 | @Test 61 | public void createConfigFromFile() { 62 | // create config by loading an empty file 63 | Config config = Config.loadConfig(new File("src/test/resources/config1.yaml")); 64 | 65 | // now check that the config contains the expected values 66 | assertAll( 67 | "config", 68 | () -> assertEquals("host1", config.getHost()), 69 | () -> assertEquals(1234, config.getPort()), 70 | () -> assertEquals(false, config.getEnabled()), 71 | () -> assertEquals("index1", config.getIndexName()), 72 | () -> assertEquals("Warning", config.getSeverity()), 73 | () -> assertTrue(config.getFilterConfigs() instanceof ArrayList), 74 | () -> assertEquals(1, config.getFilterConfigs().size()), 75 | () -> 76 | assertEquals( 77 | "FilterConfig{expression='MSGID != 'BEA-000449'', servers=[]}", 78 | config.getFilterConfigs().get(0).toString()), 79 | () -> assertEquals(2, config.getBulkSize()), 80 | () -> assertEquals("domain1", config.getDomainUID())); 81 | } 82 | 83 | @DisplayName("Config file does not exist") 84 | @Test 85 | public void configFileDoesNotExist() { 86 | Config config = Config.loadConfig(new File("src/test/resources/no-such-file.yaml")); 87 | assertTrue(outContent.toString().contains(("Not Found"))); 88 | assertTrue(outContent.toString().contains("Using default for all parameters")); 89 | } 90 | 91 | @DisplayName("Config file cannot be parsed") 92 | @Test 93 | public void configFileCannotBeParsed() { 94 | Config config = Config.loadConfig(new File("src/test/resources/bad.yaml")); 95 | assertTrue(outContent.toString().contains(("Error parsing configuration file"))); 96 | assertTrue(outContent.toString().contains("Using default for all parameters")); 97 | } 98 | 99 | @DisplayName("Should convert index name to lower case") 100 | @Test 101 | public void shouldConvertIndexNameToLowerCase() { 102 | // create config by loading an empty file 103 | Config config = Config.loadConfig(new File("src/test/resources/config2.yaml")); 104 | 105 | // now check that the config contains the expected values 106 | assertEquals("index2", config.getIndexName()); 107 | } 108 | 109 | @DisplayName("Check the toString() method works as expected") 110 | @Test 111 | public void checkToStringWorksAsExpected() { 112 | Config config = Config.loadConfig(new File("src/test/resources/config1.yaml")); 113 | System.out.println(config.toString()); 114 | assertEquals(EXPECTED_STRING, config.toString()); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/weblogic/logging/exporter/config/FilterConfigTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter.config; 5 | 6 | import static org.junit.jupiter.api.Assertions.*; 7 | 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.PrintStream; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | import org.junit.jupiter.api.AfterEach; 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.DisplayName; 16 | import org.junit.jupiter.api.Test; 17 | 18 | @DisplayName("Test FilterConfig class") 19 | public class FilterConfigTest { 20 | 21 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 22 | private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); 23 | private final PrintStream originalOut = System.out; 24 | private final PrintStream originalErr = System.err; 25 | 26 | @BeforeEach 27 | public void setUpStreams() { 28 | System.setOut(new PrintStream(outContent)); 29 | System.setErr(new PrintStream(errContent)); 30 | } 31 | 32 | @AfterEach 33 | public void restoreStreams() { 34 | System.setOut(originalOut); 35 | System.setErr(originalErr); 36 | } 37 | 38 | private static final String EXPECTED_FILTERS_STRING = 39 | "FilterConfig{expression='MSGID != 'BEA-000449'', servers=[]}"; 40 | 41 | private static final String EXPECTED_SERVERS_STRING = 42 | "FilterConfig{expression='null', servers=[managed-server-1]}"; 43 | 44 | @DisplayName("Check toString() works as expected for filter expressions") 45 | @Test 46 | public void checkToStringWorksAsExpectedForFilterExpressions() { 47 | Map map = new HashMap<>(); 48 | map.put("FilterExpression", "MSGID != 'BEA-000449'"); 49 | FilterConfig filterConfig = FilterConfig.create(map); 50 | 51 | assertEquals(EXPECTED_FILTERS_STRING, filterConfig.toString()); 52 | } 53 | 54 | @DisplayName("Check toString() works as expected for servers") 55 | @Test 56 | public void checkToStringWorksAsExpectedForServers() { 57 | Map map = new HashMap<>(); 58 | map.put("FilterServers", "managed-server-1"); 59 | FilterConfig filterConfig = FilterConfig.create(map); 60 | 61 | assertEquals(EXPECTED_SERVERS_STRING, filterConfig.toString()); 62 | } 63 | 64 | @DisplayName("Check the query can be retrieved") 65 | @Test 66 | public void checkTheQuery() { 67 | Map map = new HashMap<>(); 68 | map.put("FilterExpression", "MSGID != 'BEA-000449'"); 69 | FilterConfig filterConfig = FilterConfig.create(map); 70 | 71 | assertTrue( 72 | filterConfig.getQuery().toString().contains("weblogic.diagnostics.query.CompiledQuery")); 73 | } 74 | 75 | @DisplayName("Bad filter expression") 76 | @Test 77 | public void badFilterExpression() { 78 | Map map = new HashMap<>(); 79 | map.put("FilterExpression", "nonsense-text"); 80 | FilterConfig filterConfig = FilterConfig.create(map); 81 | 82 | assertTrue(outContent.toString().contains(("Error Parsing expression:"))); 83 | } 84 | 85 | @DisplayName("Check duplicate values are rejected") 86 | @Test 87 | public void checkDuplicateValuesAreRejected() { 88 | Map map = new HashMap<>(); 89 | map.put( 90 | "FilterServers", new String[] {"managed-server-1", "managed-server-2", "managed-server-1"}); 91 | 92 | assertThrows(ConfigurationException.class, () -> FilterConfig.create(map)); 93 | } 94 | 95 | @DisplayName("Check getServers() returns the right data") 96 | @Test 97 | public void checkGetServersWorks() { 98 | Map map = new HashMap<>(); 99 | map.put("FilterServers", new String[] {"managed-server-1", "managed-server-2"}); 100 | FilterConfig filterConfig = FilterConfig.create(map); 101 | 102 | assertTrue(filterConfig.getServers() instanceof List); 103 | assertEquals(2, filterConfig.getServers().size()); 104 | assertTrue(filterConfig.getServers().get(0) instanceof String); 105 | assertEquals("managed-server-1", filterConfig.getServers().get(0)); 106 | assertTrue(filterConfig.getServers().get(1) instanceof String); 107 | assertEquals("managed-server-2", filterConfig.getServers().get(1)); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/weblogic/logging/exporter/config/MapUtilsTest.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, 2021, Oracle and/or its affiliates. 2 | // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | package weblogic.logging.exporter.config; 5 | 6 | import static org.hamcrest.Matchers.arrayContaining; 7 | import static org.hamcrest.junit.MatcherAssert.assertThat; 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | import static org.junit.jupiter.api.Assertions.assertThrows; 10 | 11 | import java.util.Arrays; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import org.junit.jupiter.api.BeforeAll; 15 | import org.junit.jupiter.api.DisplayName; 16 | import org.junit.jupiter.api.Test; 17 | 18 | @SuppressWarnings("EmptyMethod") 19 | public class MapUtilsTest { 20 | 21 | @BeforeAll 22 | public static void setUp() {} 23 | 24 | @Test 25 | public void whenStringArrayValueIsStringArray_returnAsIs() { 26 | final String[] STRING_ARRAY = {"1", "2", "3"}; 27 | Map map = createMapWithValue(STRING_ARRAY); 28 | 29 | assertThat(MapUtils.getStringArray(map, "values"), arrayContaining(STRING_ARRAY)); 30 | } 31 | 32 | @Test 33 | public void whenStringArrayValueIsSingleObject_returnAsLengthOneArray() { 34 | Map map = createMapWithValue(33); 35 | 36 | assertThat(MapUtils.getStringArray(map, "values"), arrayContaining("33")); 37 | } 38 | 39 | @Test 40 | public void whenStringArrayValueIsList_returnAsArray() { 41 | Map map = createMapWithValue(Arrays.asList(7, 8, true)); 42 | 43 | assertThat(MapUtils.getStringArray(map, "values"), arrayContaining("7", "8", "true")); 44 | } 45 | 46 | @DisplayName("After creating a map, check that 'good' true values are correctly interpreted") 47 | @Test 48 | public void afterCreateMap_checkGoodTrueValues() { 49 | Map map = createMapOfTrueValues(); 50 | 51 | assertEquals(true, MapUtils.getBooleanValue(map, "1")); 52 | assertEquals(true, MapUtils.getBooleanValue(map, "2")); 53 | assertEquals(true, MapUtils.getBooleanValue(map, "3")); 54 | assertEquals(true, MapUtils.getBooleanValue(map, "4")); 55 | assertEquals(true, MapUtils.getBooleanValue(map, "5")); 56 | } 57 | 58 | @DisplayName("After creating a map, check that 'bad' true values are correctly interpreted") 59 | @Test 60 | public void afterCreateMap_checkBadTrueValues() { 61 | Map map = createMapOfTrueValues(); 62 | 63 | assertThrows(ConfigurationException.class, () -> MapUtils.getBooleanValue(map, "6")); 64 | assertThrows(ConfigurationException.class, () -> MapUtils.getBooleanValue(map, "7")); 65 | assertThrows(ConfigurationException.class, () -> MapUtils.getBooleanValue(map, "8")); 66 | assertThrows(ConfigurationException.class, () -> MapUtils.getBooleanValue(map, "9")); 67 | assertThrows(ConfigurationException.class, () -> MapUtils.getBooleanValue(map, "10")); 68 | } 69 | 70 | @DisplayName("After creating a map, check that 'good' false values are correctly interpreted") 71 | @Test 72 | public void afterCreateMap_checkGoodFalseValues() { 73 | Map map = createMapOfFalseValues(); 74 | 75 | assertEquals(false, MapUtils.getBooleanValue(map, "1")); 76 | assertEquals(false, MapUtils.getBooleanValue(map, "2")); 77 | assertEquals(false, MapUtils.getBooleanValue(map, "3")); 78 | assertEquals(false, MapUtils.getBooleanValue(map, "4")); 79 | assertEquals(false, MapUtils.getBooleanValue(map, "5")); 80 | } 81 | 82 | @DisplayName("After creating a map, check that 'bad' false values are correctly interpreted") 83 | @Test 84 | public void afterCreateMap_checkBadFalseValues() { 85 | Map map = createMapOfFalseValues(); 86 | 87 | assertThrows(ConfigurationException.class, () -> MapUtils.getBooleanValue(map, "6")); 88 | assertThrows(ConfigurationException.class, () -> MapUtils.getBooleanValue(map, "7")); 89 | assertThrows(ConfigurationException.class, () -> MapUtils.getBooleanValue(map, "8")); 90 | assertThrows(ConfigurationException.class, () -> MapUtils.getBooleanValue(map, "9")); 91 | assertThrows(ConfigurationException.class, () -> MapUtils.getBooleanValue(map, "10")); 92 | } 93 | 94 | private Map createMapWithValue(Object value) { 95 | Map map = new HashMap<>(); 96 | map.put("values", value); 97 | return map; 98 | } 99 | 100 | private Map createMapOfTrueValues() { 101 | Map map = new HashMap<>(); 102 | map.put("1", "true"); 103 | map.put("2", "t"); 104 | map.put("3", "yes"); 105 | map.put("4", "on"); 106 | map.put("5", "y"); 107 | map.put("6", "truenot"); 108 | map.put("7", "nottrue"); 109 | map.put("8", "yesss"); 110 | map.put("9", "y "); 111 | map.put("10", " y "); 112 | return map; 113 | } 114 | 115 | private Map createMapOfFalseValues() { 116 | Map map = new HashMap<>(); 117 | map.put("1", "false"); 118 | map.put("2", "f"); 119 | map.put("3", "no"); 120 | map.put("4", "off"); 121 | map.put("5", "n"); 122 | map.put("6", "falsedata"); 123 | map.put("7", "sofalse"); 124 | map.put("8", "ono"); 125 | map.put("9", "n "); 126 | map.put("10", " n "); 127 | return map; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/test/resources/bad.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019, 2021, Oracle and/or its affiliates. 2 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | # I am not well formated 5 | publishHost: host1 6 | something else that should 7 | - not be here 8 | at all 9 | -------------------------------------------------------------------------------- /src/test/resources/config1.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019, 2021, Oracle and/or its affiliates. 2 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | # Test config file 5 | 6 | publishHost: host1 7 | publishPort: 1234 8 | weblogicLoggingExporterFilters: 9 | - FilterExpression: MSGID != 'BEA-000449' 10 | weblogicLoggingExporterEnabled: false 11 | weblogicLoggingExporterSeverity: Warning 12 | weblogicLoggingExporterBulkSize: 2 13 | weblogicLoggingIndexName: index1 14 | domainUID: domain1 15 | -------------------------------------------------------------------------------- /src/test/resources/config2.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019, 2021, Oracle and/or its affiliates. 2 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | # Test config file 5 | # This one has an upper case index name, which should be converted to lower case 6 | 7 | publishHost: host1 8 | publishPort: 1234 9 | weblogicLoggingExporterFilters: 10 | - FilterExpression: MSGID != 'BEA-000449' 11 | weblogicLoggingExporterEnabled: false 12 | weblogicLoggingExporterSeverity: Warning 13 | weblogicLoggingExporterBulkSize: 2 14 | weblogicLoggingIndexName: INDEX2 15 | domainUID: domain1 16 | -------------------------------------------------------------------------------- /src/test/resources/config3.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019, 2021, Oracle and/or its affiliates. 2 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | # Test config file 5 | # This one has an upper case index name, which should be converted to lower case 6 | 7 | publishHost: host1 8 | publishPort: 1234 9 | weblogicLoggingExporterFilters: 10 | - FilterExpression: MSGID != 'BEA-000449' 11 | FilterServers: 12 | - managed-server-1 13 | - managed-server-2 14 | weblogicLoggingExporterEnabled: false 15 | weblogicLoggingExporterSeverity: Warning 16 | weblogicLoggingExporterBulkSize: 2 17 | weblogicLoggingIndexName: INDEX2 18 | domainUID: domain1 19 | -------------------------------------------------------------------------------- /src/test/resources/emptyConfig.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019, 2021, Oracle and/or its affiliates. 2 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 | 4 | # I am empty 5 | --------------------------------------------------------------------------------