Resolve conflicts, add stale node/index indication

This commit is contained in:
Rashid Khan 2014-02-05 16:32:28 -07:00
commit 98fd574359
58 changed files with 1978 additions and 841 deletions

1
.gitignore vendored
View file

@ -14,3 +14,4 @@
/node_modules
/build
/.aws-config.json
html_docs

View file

@ -1,98 +0,0 @@
Contributing to elasticsearch
=============================
Elasticsearch is an open source project and we love to receive contributions from our community — you! There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into Elasticsearch itself.
Bug reports
-----------
If you think you have found a bug in Elasticsearch, first make sure that you are testing against the [latest version of Elasticsearch](http://www.elasticsearch.org/download/) - your issue may already have been fixed. If not, search our [issues list](https://github.com/elasticsearch/elasticsearch/issues) on GitHub in case a similar issue has already been opened.
It is very helpful if you can prepare a reproduction of the bug. In other words, provide a small test case which we can run to confirm your bug. It makes it easier to find the problem and to fix it. Test cases should be provided as `curl` commands which we can copy and paste into a terminal to run it locally, for example:
```sh
# delete the index
curl -XDELETE localhost:9200/test
# insert a document
curl -XPUT localhost:9200/test/test/1 -d '{
"title": "test document"
}'
# this should return XXXX but instead returns YYY
curl ....
```
Provide as much information as you can. You may think that the problem lies with your query, when actually it depends on how your data is indexed. The easier it is for us to recreate your problem, the faster it is likely to be fixed.
Feature requests
----------------
If you find yourself wishing for a feature that doesn't exist in Elasticsearch, you are probably not alone. There are bound to be others out there with similar needs. Many of the features that Elasticsearch has today have been added because our users saw the need.
Open an issue on our [issues list](https://github.com/elasticsearch/elasticsearch/issues) on GitHub which describes the feature you would like to see, why you need it, and how it should work.
Contributing code and documentation changes
-------------------------------------------
If you have a bugfix or new feature that you would like to contribute to Elasticsearch, please find or open an issue about it first. Talk about what you would like to do. It may be that somebody is already working on it, or that there are particular issues that you should know about before implementing the change.
We enjoy working with contributors to get their code accepted. There are many approaches to fixing a problem and it is important to find the best approach before writing too much code.
The process for contributing to any of the [Elasticsearch repositories](https://github.com/elasticsearch/) is similar. Details for individual projects can be found below.
### Fork and clone the repository
You will need to fork the main Elasticsearch code or documentation repository and clone it to your local machine. See
[github help page](https://help.github.com/articles/fork-a-repo) for help.
Further instructions for specific projects are given below.
### Submitting your changes
Once your changes and tests are ready to submit for review:
1. Test your changes
Run the test suite to make sure that nothing is broken.
2. Sign the Contributor License Agreement
Please make sure you have signed our [Contributor License Agreement](http://www.elasticsearch.org/contributor-agreement/). We are not asking you to assign copyright to us, but to give us the right to distribute your code without restriction. We ask this of all contributors in order to assure our users of the origin and continuing existence of the code. You only need to sign the CLA once.
3. Rebase your changes
Update your local repository with the most recent code from the main Elasticsearch repository, and rebase your branch on top of the latest master branch. We prefer your changes to be squashed into a single commit.
4. Submit a pull request
Push your local changes to your forked copy of the repository and [submit a pull request](https://help.github.com/articles/using-pull-requests). In the pull request, describe what your changes do and mention the number of the issue where discussion has taken place, eg "Closes #123".
Then sit back and wait. There will probably be discussion about the pull request and, if any changes are needed, we would love to work with you to get your pull request merged into Elasticsearch.
Contributing to the Elasticsearch plugin
----------------------------------------
**Repository:** [https://github.com/elasticsearch/elasticsearch-dash](https://github.com/elasticsearch/elasticsearch-dash)
Make sure you have [Maven](http://maven.apache.org) installed, as Elasticsearch uses it as its build system. Integration with IntelliJ and Eclipse should work out of the box. Eclipse users can automatically configure their IDE by running `mvn eclipse:eclipse` and then importing the project into their workspace: `File > Import > Existing project into workspace`.
Please follow these formatting guidelines:
* Java indent is 4 spaces
* Line width is 140 characters
* The rest is left to Java coding standards
* Disable “auto-format on save” to prevent unnecessary format changes. This makes reviews much harder as it generates unnecessary formatting changes. If your IDE supports formatting only modified chunks that is fine to do.
To create a distribution from the source, simply run:
```sh
cd elasticsearch-dash/
mvn clean package -DskipTests
```
You will find the newly built packages under: `./target/releases/`.
Before submitting your changes, run the test suite to make sure that nothing is broken, with:
```sh
mvn clean test
```
Source: [Contributing to elasticsearch](http://www.elasticsearch.org/contributing-to-elasticsearch/)

View file

@ -5,7 +5,7 @@ module.exports = function (grunt) {
var config = {
pkg: grunt.file.readJSON('package.json'),
kibanaCheckoutDir: './kibana/vendor/kibana',
kibanaRevision: 'master',
kibanaRevision: 'marvel',
agentDir: 'agent',
buildDir: 'build',
packageDir: 'build/packages',
@ -19,7 +19,7 @@ module.exports = function (grunt) {
dist: '"https://marvel-stats.elasticsearch.com/"'
},
kibanaPort: grunt.option('port') || 5601,
kibanaHost: 'localhost'
kibanaHost: grunt.option('host') ||'localhost'
};
// more detailed config

View file

@ -1,207 +1,204 @@
Software License Agreement
Marvel Software License Agreement
READ THIS AGREEMENT CAREFULLY, WHICH CONSITUTES A LEGALLY BINIDNG AGREEMENT AND
GOVERNS YOUR USE OF THE ELASTICSEARCH SOFTWARE. BY DOWNLOADING, INSTALLING
AND/OR USING THE ELASTICSEARCH SOFTWARE, YOU ARE INDICATING THAT YOU AGREE TO
THE TERMS AND CONDITIONS SET FORTH IN THIS AGREEMENT. IF YOU DO NOT AGREE WITH
SUCH TERMS AND CONDITIONS, YOU MAY NOT DOWNLOAD, INSTALL OR USE THE
ELASTICSEARCH SOFTWARE, AND YOU MAY RETURN THE ELASTICSEARCH SOFTWARE FOR A
REFUND OF ANY LICENSE FEES YOU HAVE PAID, PROVIDED THAT YOU HAVE NOT INSTALLED
OR USED SUCH ELASTICSEARCH SOFTWARE.
READ THIS AGREEMENT CAREFULLY, WHICH CONSITUTES A LEGALLY BINDING AGREEMENT
AND GOVERNS YOUR USE OF ELASTICSEARCHS MARVEL SOFTWARE. BY INSTALLING AND/OR
USING THE MARVEL SOFTWARE, YOU ARE INDICATING THAT YOU AGREE TO THE TERMS AND
CONDITIONS SET FORTH IN THIS AGREEMENT. IF YOU DO NOT AGREE WITH SUCH TERMS
AND CONDITIONS, YOU MAY NOT INSTALL OR USE THE MARVEL SOFTWARE.
This Software License Agreement (this “Agreement") is entered into by and
between the applicable Elasticsearch entity referred to in Attachment 1 below
(“Elasticsearch”) and the person or entity (“You”) that has downloaded the
Elasticsearch software to which this Agreement is attached
(“Elasticsearch Software”), and is effective as of the date you download the
Elasticsearch Software (the “Effective Date”).
This Marvel Software License Agreement (this “Agreement") is entered into by
and between the applicable Elasticsearch entity referred to in Attachment 1
below (“Elasticsearch”) and the person or entity (“You”) that has downloaded
Elasticsearchs Marvel software to which this Agreement is attached (“Marvel
Software”), and is effective as of the date you first install the Marvel
Software (the “Effective Date”).
1. Software License and Restrictions
1.1. License Grants. Subject to the terms and conditions of this Agreement and
1.1 License Grants. Subject to the terms and conditions of this Agreement and
complete payment of any and all applicable license fees (provided that no
license fee shall be required for use of the Elasticsearch Software for purely
development purposes), Elasticsearch agrees to grant, and does hereby grant to
license fee shall be required for use of the Marvel Software for other than
production purposes, Elasticsearch agrees to grant, and does hereby grant to
You during the term and for the restricted scope of this Agreement, solely for
Your internal business operations, a limited, non-exclusive, non-transferable
right and license (without the right to grant or authorize sublicenses) to: (i)
install and use the object code version of the Elasticsearch Software, subject
to any applicable quantitative limitations set forth in any order form executed
right and license (without the right to grant or authorize sublicenses) to:
(i) install and use the object code version of the Marvel Software, subject to
any applicable quantitative limitations set forth in any order form executed
by Elasticsearch and You (“Order Form”); (ii) use, and distribute internally a
reasonable number of copies of the documentation, if any, provided with the
Elasticsearch Software (“Documentation”), provided that You must include on such
Marvel Software (“Documentation”), provided that You must include on such
copies all Elasticsearch trademarks, trade names, logos and notices present on
the Documentation as originally provided to You by Elasticsearch; (iii) permit
third party contractors performing services on Your behalf to use the
Elasticsearch Software and Documentation as set forth in (i) and (ii) above,
provided that such use must be solely for Your benefit, and You shall be
responsible for all acts and omissions of such contractors in connection with
their use of the Elasticsearch Software.
third party contractors performing services on Your behalf to use the Marvel
Software and Documentation as set forth in (i) and (ii) above, provided that
such use must be solely for Your benefit, and You shall be responsible for all
acts and omissions of such contractors in connection with their use of the
Marvel Software.
1.2. Reservation of Rights; Restrictions. As between Elasticsearch and You,
Elasticsearch owns all right title and interest in and to the Elasticsearch
Software and any derivative works thereof, and except as expressly set forth in
Section 1.1 above, no other license to the Elasticsearch Software is granted to
You by implication, estoppel or otherwise. You agree not to: (i) prepare
derivative works from, modify, copy or use the Elasticsearch Software in any
manner except as expressly permitted in this Agreement or applicable law; (ii)
transfer, sell, rent, lease, distribute, sublicense, loan or otherwise transfer
the Elasticsearch Software in whole or in part to any third party; (iii) use the
Elasticsearch Software for providing time-sharing services, any software-
as-a-service offering (“SaaS”), service bureau services or as part of an
application services provider or other service offering; (iv) alter or remove
any proprietary notices in the Elasticsearch Software; or (v) make available to
any third party any analysis of the results of operation of the Elasticsearch
Software, including benchmarking results, without the prior written consent of
Elasticsearch.
1.2 Reservation of Rights; Restrictions. As between Elasticsearch and You,
Elasticsearch owns all right title and interest in and to the Marvel Software
and any derivative works thereof, and except as expressly set forth in Section
1.1 above, no other license to the Marvel Software is granted to You by
implication, estoppel or otherwise. You agree not to: (i) prepare derivative
works from, modify, copy or use the Marvel Software in any manner except as
expressly permitted in this Agreement or applicable law; (ii) transfer, sell,
rent, lease, distribute, sublicense, loan or otherwise transfer the Marvel
Software in whole or in part to any third party; (iii) use the Marvel Software
for providing time-sharing services, any software-as-a-service offering
(“SaaS”), service bureau services or as part of an application services
provider or other service offering; (iv) alter or remove any proprietary
notices in the Marvel Software; or (v) make available to any third party any
analysis of the results of operation of the Marvel Software, including
benchmarking results, without the prior written consent of Elasticsearch.
2. Term and Termination
2.1 Term. This Agreement shall commence on the Effective Date, and shall
2.1 Term. This Agreement shall commence on the Effective Date, and shall
continue in force for the license term set forth in the applicable Order Form,
unless earlier terminated under Section 2.2 below.
2.2 Termination. Either party may, upon written notice to the other party,
2.2 Termination. Either party may, upon written notice to the other party,
terminate this Agreement for material breach by the other party automatically
and without any other formality, if such party has failed to cure such material
breach within thirty (30) days of receiving written notice of such material
breach from the non-breaching party. Notwithstanding the foregoing, this
Agreement shall automatically terminate in the event that You intentionally
breach the scope of the license granted in Section 1.1 of this Agreement.
and without any other formality, if such party has failed to cure such
material breach within thirty (30) days of receiving written notice of such
material breach from the non-breaching party. Notwithstanding the foregoing,
this Agreement shall automatically terminate in the event that You
intentionally breach the scope of the license granted in Section 1.1 of this
Agreement.
2.3 Post Termination or Expiration. Upon termination or expiration of this
Agreement, for any reason, You shall promptly cease the use of the Elasticsearch
Software and Documentation and destroy (and certify to Elasticsearch in writing
the fact of such destruction), or return to Elasticsearch, all copies of the
Elasticsearch Software and Documentation then in Your possession or under Your
2.3 Post Termination or Expiration. Upon termination or expiration of this
Agreement, for any reason, You shall promptly cease the use of the Marvel
Software and Documentation and destroy (and certify to Elasticsearch in
writing the fact of such destruction), or return to Elasticsearch, all copies
of the Marvel Software and Documentation then in Your possession or under Your
control.
2.4 Survival.
Sections 2.3, 3, 4 and 5, as well as any payment obligations under this
Agreement and/or the Order Form, shall survive any termination or expiration of
this Agreement.
2.4 Survival. Sections 2.3, 3, 4 and 5, as well as any payment obligations
under this Agreement and/or the Order Form, shall survive any termination or
expiration of this Agreement.
3. Disclaimer of Warranties
TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE ELASTICSEARCH SOFTWARE
IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS
TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE MARVEL SOFTWARE IS
PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS
LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR STATUTORY REGARDING
OR RELATING TO THE ELASTICSEARCH SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM
EXTENT PERMITTED UNDER APPLICABLE LAW, ELASTICSEARCH AND ITS LICENSORS
SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NON-INFRINGMENT WITH RESPECT TO THE ELASTICSEARCH
SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF THE FOREGOING.
FURTHER, ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT THE ELASTICSEARCH
SOFTWARE WILL BE ERROR FREE OR THAT THE USE OF THE ELASTICSEARCH SOFTWARE WILL
BE UNINTERRUPTED.
OR RELATING TO THE MARVEL SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT
PERMITTED UNDER APPLICABLE LAW, ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY
DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NON-INFRINGMENT WITH RESPECT TO THE MARVEL SOFTWARE AND
DOCUMENTATION, AND WITH RESPECT TO THE USE OF THE FOREGOING. FURTHER,
ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT THE MARVEL SOFTWARE WILL
BE ERROR FREE OR THAT THE USE OF THE MARVEL SOFTWARE WILL BE UNINTERRUPTED.
4. Limitation of Liability
4.1. Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR
ITS LICENSORS BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF USE, BUSINESS
INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY
INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION
WITH OR ARISING OUT OF THE USE OR INABILITY TO USE THE ELASTICSEARCH SOFTWARE,
OR THE PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A
BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, EVEN IF THE
RESPONSIBLE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE
LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A
BREACH THROUGH GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU OF THE SCOPE OF
THE LICENSE GRANTED IN SECTION 1.1 OR TO ANY OTHER LIABILITY THAT CANNOT BE
EXCLUDED OR LIMITED UNDER APPLICABLE LAW.
INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND IN
CONNECTION WITH OR ARISING OUT OF THE USE OR INABILITY TO USE THE MARVEL
SOFTWARE, OR THE PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER
ALLEGED AS A BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE,
EVEN IF THE RESPONSIBLE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT
APPLY TO A BREACH THROUGH GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU OF
THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1 OR TO ANY OTHER LIABILITY THAT
CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.
4.2. Damages Cap. IN NO EVENT SHALL ELASTICSEARCHS OR ITS LICENSORS AGGREGATE,
CUMULATIVE LIABILITY UNDER THIS AGREEMENT EXCEED THE AMOUNTS YOU WERE REQUIRED
TO PAY ELASTICSEARCH IN RELATION TO THIS AGREEMENT FOR THE ELASTICSEARCH
SOFTWARE GIVING RISE TO SUCH LIABILITY, IN THE TWELVE (12) MONTHS IMMEDIATELY
PRIOR TO THE EVENT GIVING RISE TO LIABILITY.
4.2 Damages Cap. IN NO EVENT SHALL ELASTICSEARCHS OR ITS LICENSORS
AGGREGATE, CUMULATIVE LIABILITY UNDER THIS AGREEMENT EXCEED THE AMOUNTS YOU
WERE REQUIRED TO PAY ELASTICSEARCH IN RELATION TO THIS AGREEMENT FOR THE
MARVEL SOFTWARE GIVING RISE TO SUCH LIABILITY, IN THE TWELVE (12) MONTHS
IMMEDIATELY PRIOR TO THE EVENT GIVING RISE TO LIABILITY.
4.3. YOU AGREE THAT THE FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS ARE A
4.3 YOU AGREE THAT THE FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS ARE A
REASONABLE ALLOCATION OF THE RISK BETWEEN THE PARTIES AND WILL APPLY TO THE
MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS
ESSENTIAL PURPOSE.
5. Miscellaneous
5. Miscellaneous
This Agreement, including Attachment 1 hereto, which is hereby incorporated
herein by this reference, completely and exclusively states the entire agreement
of the parties regarding the subject matter herein, and it supersedes, and its
terms govern, all prior proposals, agreements, or other communications between
the parties, oral or written, regarding such subject matter. For the avoidance
of doubt, the parties hereby expressly acknowledge and agree that if You issue
any purchase order or similar document in connection with its purchase of a
license to the Elasticsearch Software, You will do so only for Your internal,
administrative purposes and not with the intent to provide any contractual
terms. This Agreement may not be modified except by a subsequently dated,
written amendment that expressly amends this Agreement and which is signed on
behalf of Elasticsearch and You, by duly authorized representatives. If any
provision(s) hereof is held unenforceable, this Agreement will continue without
said provision and be interpreted to reflect the original intent of the parties.
herein by this reference, completely and exclusively states the entire
agreement of the parties regarding the subject matter herein, and it
supersedes, and its terms govern, all prior proposals, agreements, or other
communications between the parties, oral or written, regarding such subject
matter. For the avoidance of doubt, the parties hereby expressly acknowledge
and agree that if You issue any purchase order or similar document in
connection with its purchase of a license to the Marvel Software, You will do
so only for Your internal, administrative purposes and not with the intent to
provide any contractual terms. This Agreement may not be modified except by a
subsequently dated, written amendment that expressly amends this Agreement and
which is signed on behalf of Elasticsearch and You, by duly authorized
representatives. If any provision(s) hereof is held unenforceable, this
Agreement will continue without said provision and be interpreted to reflect
the original intent of the parties.
ATTACHMENT 1
ADDITIONAL TERMS AND CONDITIONS
ATTACHMENT 1
A The following additional terms and conditions apply to all Customers with
ADDITIONAL TERMS AND CONDITIONS
A The following additional terms and conditions apply to all Customers with
principal offices in North America:
Applicable Elasticsearch Entity. The entity providing the license is
Elasticsearch, Inc., a Delaware Corporation.
Government Rights. The Elasticsearch Software product is "Commercial Computer
Government Rights. The Marvel Software product is "Commercial Computer
Software," as that term is defined in 48 C.F.R. 2.101, and as the term is used
in 48 C.F.R. Part 12, and is a Commercial Item comprised of "commercial computer
software" and "commercial computer software documentation". If acquired by or
on behalf of a civilian agency, the U.S. Government acquires this commercial
computer software and/or commercial computer software documentation subject to
the terms of this Agreement, as specified in 48 C.F.R. 12.212 (Computer
Software) and 12.211 (Technical Data) of the Federal Acquisition Regulation
("FAR") and its successors. If acquired by or on behalf of any agency within
the Department of Defense ("DOD"), the U.S. Government acquires this commercial
computer software and/or commercial computer software documentation subject to
the terms of the Elasticsearch Software License and Services Agreement as
specified in 48 C.F.R. 227.7202-3 and 48 C.F.R. 227.7202-4 of the DOD FAR
Supplement ("DFARS") and its successors, and consistent with 48 C.F.R. 227.7202.
This U.S. Government Rights clause, consistent with 48 C.F.R. 12.212 and 48
C.F.R. 227.7202 is in lieu of, and supersedes, any other FAR, DFARS, or other
clause or provision that addresses Government rights in computer software,
computer software documentation or technical data related to the Elasticsearch
Software under this Agreement and in any Subcontract under which this commercial
computer software and commercial computer software documentation is acquired or
licensed.
in 48 C.F.R. Part 12, and is a Commercial Item comprised of "commercial
computer software" and "commercial computer software documentation". If
acquired by or on behalf of a civilian agency, the U.S. Government acquires
this commercial computer software and/or commercial computer software
documentation subject to the terms of this Agreement, as specified in 48
C.F.R. 12.212 (Computer Software) and 12.211 (Technical Data) of the Federal
Acquisition Regulation ("FAR") and its successors. If acquired by or on
behalf of any agency within the Department of Defense ("DOD"), the U.S.
Government acquires this commercial computer software and/or commercial
computer software documentation subject to the terms of the Elasticsearch
Software License Agreement as specified in 48 C.F.R. 227.7202-3 and 48 C.F.R.
227.7202-4 of the DOD FAR Supplement ("DFARS") and its successors, and
consistent with 48 C.F.R. 227.7202. This U.S. Government Rights clause,
consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202 is in lieu of, and
supersedes, any other FAR, DFARS, or other clause or provision that addresses
Government rights in computer software, computer software documentation or
technical data related to the Marvel Software under this Agreement and in any
Subcontract under which this commercial computer software and commercial
computer software documentation is acquired or licensed.
Export Control. You acknowledge that the goods, software and technology acquired
from Elasticsearch are subject to U.S. export control laws and regulations,
including but not limited to the International Traffic In Arms Regulations
(“ITAR”) (22 C.F.R. Parts 120-130 (2010)); the Export Administration Regulations
("EAR") (15 C.F.R. Parts 730-774 (2010)); the U.S. antiboycott regulations in
the EAR and U.S. Department of the Treasury regulations; the economic sanctions
regulations and guidelines of the U.S. Department of the Treasury, Office of
Foreign Assets Control, and the USA Patriot Act (Title III of Pub. L. 107-56,
signed into law October 26, 2001), as amended.  You are now and will remain in
the future compliant with all such export control laws and regulations, and will
not export, re-export, otherwise transfer any Elasticsearch goods, software or
technology or disclose any Elasticsearch software or technology to any person
contrary to such laws or regulations.  You acknowledge that remote access to
Elasticsearch Software may in certain circumstances be considered a re-export of
Elasticsearch Software, and accordingly, may not be granted in contravention of
U.S. export control laws and regulations. Governing Law. This Agreement will be
governed by the laws of the State of California, without regard to its conflict
of laws principles. This Agreement shall not be governed by the 1980 UN
Convention on Contracts for the International Sale of Goods. All suits hereunder
will be brought solely in Federal Court for the Northern District of California,
or if that court lacks subject matter jurisdiction, in any California State
Court located in Santa Clara County. The parties hereby irrevocably waive any
and all claims and defenses either might otherwise have in any such action or
proceeding in any of such courts based upon any alleged lack of personal
jurisdiction, improper venue, forum non conveniens or any similar claim or
defense.
Export Control. You acknowledge that the goods, software and technology
acquired from Elasticsearch are subject to U.S. export control laws and
regulations, including but not limited to the International Traffic In Arms
Regulations (“ITAR”) (22 C.F.R. Parts 120-130 (2010)); the Export
Administration Regulations ("EAR") (15 C.F.R. Parts 730-774 (2010)); the U.S.
antiboycott regulations in the EAR and U.S. Department of the Treasury
regulations; the economic sanctions regulations and guidelines of the U.S.
Department of the Treasury, Office of Foreign Assets Control, and the USA
Patriot Act (Title III of Pub. L. 107-56, signed into law October 26, 2001),
as amended.  You are now and will remain in the future compliant with all such
export control laws and regulations, and will not export, re-export, otherwise
transfer any Elasticsearch goods, software or technology or disclose any
Elasticsearch software or technology to any person contrary to such laws or
regulations.  You acknowledge that remote access to the Marvel Software may in
certain circumstances be considered a re-export of Marvel Software, and
accordingly, may not be granted in contravention of U.S. export control laws
and regulations.
Governing Law. This Agreement will be governed by the laws of the State of
California, without regard to its conflict of laws principles. This Agreement
shall not be governed by the 1980 UN Convention on Contracts for the
International Sale of Goods. All suits hereunder will be brought solely in
Federal Court for the Northern District of California, or if that court lacks
subject matter jurisdiction, in any California State Court located in Santa
Clara County. The parties hereby irrevocably waive any and all claims and
defenses either might otherwise have in any such action or proceeding in any
of such courts based upon any alleged lack of personal jurisdiction, improper
venue, forum non conveniens or any similar claim or defense.
B The following additional terms and conditions apply to all Customers with
B The following additional terms and conditions apply to all Customers with
principal offices outside of North America:
(1) Applicable Elasticsearch Entity. The entity providing the license in
@ -210,47 +207,49 @@ principal offices outside of North America:
(2) Choice of Law. This Agreement shall be governed by and construed in
(accordance with the laws of the State of New York, without reference to or
(application of choice of law rules or principles. Notwithstanding any choice of
(law provision or otherwise, the Uniform Computer Information Transactions Act
(UCITA) and the United Nations Convention on the International Sale of Goods
(shall not apply.
(application of choice of law rules or principles. Notwithstanding any choice
(of law provision or otherwise, the Uniform Computer Information Transactions
(Act UCITA) and the United Nations Convention on the International Sale of
(Goods shall not apply.
(3) Arbitration. Any dispute, claim or controversy arising out of or relating
(to this Agreement or the existence, breach, termination, enforcement,
(interpretation or validity thereof, including the determination of the scope or
(applicability of this agreement to arbitrate, each, a “Dispute”) shall be
(referred to and finally resolved by arbitration under the rules and at the
(location identified below. The arbitral panel shall consist of three 3)
(arbitrators, selected as follows: each party shall appoint one 1) arbitrator;
(and those two 2) arbitrators shall discuss and select a chairman. If the two
(party-appointed arbitrators are unable to agree on the chairman, the chairman
(shall be selected in accordance with the applicable rules of the arbitration
(body. Each arbitrator shall be independent of each of the parties. The
(arbitrators shall have the authority to grant specific performance and to
(allocate between the parties the costs of arbitration including service fees,
(arbitrator fees and all other fees related to the arbitration) in such
(equitable manner as the arbitrators may determine. The prevailing party in any
(arbitration shall be entitled to receive reimbursement of its reasonable
(expenses incurred in connection therewith. Judgment upon the award so rendered
(may be entered in a court having jurisdiction or application may be made to
(such court for judicial acceptance of any award and an order of enforcement, as
(the case may be. Notwithstanding the forgoing, Elasticsearch shall have the
(right to institute an action in a court of proper jurisdiction for preliminary
(injunctive relief pending a final decision by the arbitrator, provided that a
(permanent injunction and damages shall only be awarded by the arbitrator. The
(language to be used in the arbitral proceedings shall be English.
(3) Arbitration. Any dispute, claim or controversy arising out of or
(relating to this Agreement or the existence, breach, termination,
(enforcement, interpretation or validity thereof, including the determination
(of the scope or applicability of this agreement to arbitrate, each, a
(“Dispute”) shall be referred to and finally resolved by arbitration under the
(rules and at the location identified below. The arbitral panel shall consist
(of three 3) arbitrators, selected as follows: each party shall appoint one 1)
(arbitrator; and those two 2) arbitrators shall discuss and select a chairman.
(If the two party-appointed arbitrators are unable to agree on the chairman,
(the chairman shall be selected in accordance with the applicable rules of the
(arbitration body. Each arbitrator shall be independent of each of the
(parties. The arbitrators shall have the authority to grant specific
(performance and to allocate between the parties the costs of arbitration
(including service fees, arbitrator fees and all other fees related to the
(arbitration) in such equitable manner as the arbitrators may determine. The
(prevailing party in any arbitration shall be entitled to receive
(reimbursement of its reasonable expenses incurred in connection therewith.
(Judgment upon the award so rendered may be entered in a court having
(jurisdiction or application may be made to such court for judicial acceptance
(of any award and an order of enforcement, as the case may be.
(Notwithstanding the forgoing, Elasticsearch shall have the right to institute
(an action in a court of proper jurisdiction for preliminary injunctive relief
(pending a final decision by the arbitrator, provided that a permanent
(injunction and damages shall only be awarded by the arbitrator. The language
(to be used in the arbitral proceedings shall be English.
(3.a) In addition, the following terms only apply to Customers with principal
(offices within Europe, the Middle East or Africa EMEA):
(3.a) In addition, the following terms only apply to Customers with
(principal offices within Europe, the Middle East or Africa EMEA):
Arbitration Rules and Location. Any Dispute shall be referred to and finally
Arbitration Rules and Location. Any Dispute shall be referred to and finally
resolved by arbitration under the London Court of International Arbitration
(“LCIA”) Rules (which Rules are deemed to be incorporated by reference into this
clause) on the basis that the governing law is the law of the State of New York,
USA. The seat, or legal place, of arbitration shall be London, England.
(“LCIA”) Rules (which Rules are deemed to be incorporated by reference into
this clause) on the basis that the governing law is the law of the State of
New York, USA. The seat, or legal place, of arbitration shall be London,
England.
(3.b) In addition, the following terms only apply to Customers with principal
(offices within Asia Pacific, Australia & New Zealand:
(3.b) In addition, the following terms only apply to Customers with
(principal offices within Asia Pacific, Australia & New Zealand:
Arbitration Rules and Location. Any Dispute shall be referred to and finally
resolved by arbitration under the Rules of Conciliation and Arbitration of the
@ -260,74 +259,71 @@ deemed to be incorporated by reference into this clause) on the basis that the
governing law is the law of the State of New York, USA. The seat, or legal
place, of arbitration shall be Singapore.
(3.c) In addition, the following terms only apply to Customers with principal
(offices within the Americas excluding North America):
(3.c) In addition, the following terms only apply to Customers with
(principal offices within the Americas excluding North America):
Arbitration Rules and Location. Any Dispute shall be referred to and finally
resolved by arbitration under International Dispute Resolution Procedures of the
American Arbitration Association (“AAA”) in force on the date when the notice of
arbitration is submitted in accordance with such Procedures (which Procedures
are deemed to be incorporated by reference into this clause) on the basis that
the governing law is the law of the State of New York, USA. The seat, or legal
place, of arbitration shall be New York, New York, USA.
Arbitration Rules and Location. Any Dispute shall be referred to and finally
resolved by arbitration under International Dispute Resolution Procedures of
the American Arbitration Association (“AAA”) in force on the date when the
notice of arbitration is submitted in accordance with such Procedures (which
Procedures are deemed to be incorporated by reference into this clause) on the
basis that the governing law is the law of the State of New York, USA. The
seat, or legal place, of arbitration shall be New York, New York, USA.
(4) In addition, for Customers with principal offices within the UK, the
(following new sentence is added to the end of Section 4.1:
Nothing in this Agreement shall have effect so as to limit or exclude a partys
liability for death or personal injury caused by negligence or for fraud
including fraudulent misrepresentation and this Section 4.1 shall take effect
subject to this provision.
Nothing in this Agreement shall have effect so as to limit or exclude a
partys liability for death or personal injury caused by negligence or for
fraud including fraudulent misrepresentation and this Section 4.1 shall take
effect subject to this provision.
(5) In addition, for Customers with principal offices within France, Sections
1.2, 3 and 4.1 of the Agreement are deleted and replaced with the following new
Sections 1.2, 3 and 4.1: 1.2 Reservation of Rights; Restrictions. Elasticsearch
owns all right title and interest in and to the Elasticsearch Software and any
derivative works thereof, and except as expressly set forth in Section 1.1
above, no other license to the Elasticsearch Software is granted to You by
implication, or otherwise. You agree not to prepare derivative works from,
modify, copy or use the Elasticsearch Software in any manner except as expressly
permitted in this Agreement; provided that You may copy the Elasticsearch
Software for archival purposes, only where such software is provided on a non-
durable medium; and You may decompile the Elasticsearch Software, where
necessary for interoperability purposes and where necessary for the correction
of errors making the software unfit for its intended purpose, if such right is
not reserved by Elasticsearch as editor of the Elasticsearch Software. Pursuant
to article L122-6-1 of the French intellectual property code, Elasticsearch
reserves the right to correct any bugs as necessary for the Elasticsearch
Software to serve its intended purpose. You agree not to: (i) transfer, sell,
rent, lease, distribute, sublicense, loan or otherwise transfer the
Elasticsearch Software in whole or in part to any third party; (ii) use the
Elasticsearch Software for providing time-sharing services, any software-
(5) In addition, for Customers with principal offices within France, Sections
(1.2, 3 and 4.1 of the Agreement are deleted and replaced with the following
(new Sections 1.2, 3 and 4.1:
1.2 Reservation of Rights; Restrictions. Elasticsearch owns all right title
and interest in and to the Marvel Software and any derivative works thereof,
and except as expressly set forth in Section 1.1 above, no other license to
the Marvel Software is granted to You by implication, or otherwise. You agree
not to prepare derivative works from, modify, copy or use the Marvel Software
in any manner except as expressly permitted in this Agreement; provided that
You may copy the Marvel Software for archival purposes, only where such
software is provided on a non-durable medium; and You may decompile the Marvel
Software, where necessary for interoperability purposes and where necessary
for the correction of errors making the software unfit for its intended
purpose, if such right is not reserved by Elasticsearch as editor of the
Marvel Software. Pursuant to article L122-6-1 of the French intellectual
property code, Elasticsearch reserves the right to correct any bugs as
necessary for the Marvel Software to serve its intended purpose. You agree not
to: (i) transfer, sell, rent, lease, distribute, sublicense, loan or otherwise
transfer the Marvel Software in whole or in part to any third party; (ii) use
the Marvel Software for providing time-sharing services, any software-
as-a-service offering (“SaaS”), service bureau services or as part of an
application services provider or other service offering; (iii) alter or remove
any proprietary notices in the Elasticsearch Software; or (iv) make available to
any third party any analysis of the results of operation of the Elasticsearch
Software, including benchmarking results, without the prior written consent of
any proprietary notices in the Marvel Software; or (iv) make available to any
third party any analysis of the results of operation of the Marvel Software,
including benchmarking results, without the prior written consent of
Elasticsearch.
3. Disclaimer of Warranties
TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE ELASTICSEARCH
SOFTWARE IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY
KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER
EXPRESSED, IMPLIED OR STATUTORY REGARDING OR RELATING TO THE ELASTICSEARCH
SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE
LAW, ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED
WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE WITH RESPECT TO THE
ELASTICSEARCH SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF THE
FOREGOING. FURTHER, ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT
THE ELASTICSEARCH SOFTWARE WILL BE ERROR FREE OR THAT THE USE OF THE
ELASTICSEARCH SOFTWARE WILL BE UNINTERRUPTED.
3. Disclaimer of Warranties TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE
LAW, THE MARVEL SOFTWARE IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, AND
ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED
OR STATUTORY REGARDING OR RELATING TO THE MARVEL SOFTWARE OR DOCUMENTATION.
TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, ELASTICSEARCH AND ITS
LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF FITNESS FOR A
PARTICULAR PURPOSE WITH RESPECT TO THE MARVEL SOFTWARE AND DOCUMENTATION, AND
WITH RESPECT TO THE USE OF THE FOREGOING. FURTHER, ELASTICSEARCH DOES NOT
WARRANT RESULTS OF USE OR THAT THE MARVEL SOFTWARE WILL BE ERROR FREE OR THAT
THE USE OF THE MARVEL SOFTWARE WILL BE UNINTERRUPTED.
4.1 Disclaimer of Certain Damages.
IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY
LOSS OF PROFITS, LOSS OF USE, BUSINESS
INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY
INDIRECT OR UNFORESEEABLE DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING
OUT OF THE USE OR INABILITY TO USE THE ELASTICSEARCH SOFTWARE, OR THE
PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A
BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE. THE
LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A
BREACH, THROUGH GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU, OF THE
SCOPE OF THE LICENSE GRANTED IN SECTION 1.1, OR IN CASE OF DEATH OR PERSONAL
INJURY.
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR
ITS LICENSORS BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF USE, BUSINESS
INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY
INDIRECT OR UNFORESEEABLE DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING
OUT OF THE USE OR INABILITY TO USE THE MARVEL SOFTWARE, OR THE PERFORMANCE OF
OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A BREACH OF CONTRACT
OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE. THE LIMITATIONS OF LIABILITY SET
FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH, THROUGH GROSS
NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU, OF THE SCOPE OF THE LICENSE
GRANTED IN SECTION 1.1, OR IN CASE OF DEATH OR PERSONAL INJURY.

View file

@ -6,16 +6,12 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.elasticsearch</groupId>
<artifactId>marvel</artifactId>
<version>1.0.0.RC2-SNAPSHOT</version>
<version>1.0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<description>Elasticsearch Marvel</description>
<inceptionYear>2013</inceptionYear>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<parent>

View file

@ -195,7 +195,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
}
private void exportIndicesStats() {
logger.debug("local node is master, exporting indices stats");
logger.trace("local node is master, exporting indices stats");
IndicesStatsResponse indicesStatsResponse = client.admin().indices().prepareStats().all().get();
for (Exporter e : exporters) {
try {
@ -207,7 +207,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
}
private void exportClusterStats() {
logger.debug("local node is master, exporting cluster stats");
logger.trace("local node is master, exporting cluster stats");
ClusterStatsResponse stats = client.admin().cluster().prepareClusterStats().get();
for (Exporter e : exporters) {
try {
@ -219,7 +219,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
}
private void exportEvents() {
logger.debug("exporting events");
logger.trace("exporting events");
ArrayList<Event> eventList = new ArrayList<Event>(pendingEventsQueue.size());
pendingEventsQueue.drainTo(eventList);
Event[] events = new Event[eventList.size()];
@ -235,7 +235,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
}
private void exportShardStats() {
logger.debug("Collecting shard stats");
logger.trace("Collecting shard stats");
String[] indices = clusterService.state().metaData().concreteIndices(indicesToExport);
List<ShardStats> shardStats = newArrayList();
@ -254,7 +254,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
}
ShardStats[] shardStatsArray = shardStats.toArray(new ShardStats[shardStats.size()]);
logger.debug("Exporting shards stats");
logger.trace("Exporting shards stats");
for (Exporter e : exporters) {
try {
e.exportShardStats(shardStatsArray);
@ -265,10 +265,10 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
}
private void exportNodeStats() {
logger.debug("Collecting node stats");
logger.trace("Collecting node stats");
NodeStats nodeStats = nodeService.stats();
logger.debug("Exporting node stats");
logger.trace("Exporting node stats");
for (Exporter e : exporters) {
try {
e.exportNodeStats(nodeStats);

View file

@ -37,13 +37,19 @@ public class Plugin extends AbstractPlugin {
final ESLogger logger = Loggers.getLogger(getClass());
// copied here because we can't depend on it available
public static final int V_0_90_8_ID = /*00*/900899;
public static final int V_0_90_9_ID = /*00*/900999;
public static final int V_1_0_0_Beta1_ID = 1000001;
public static final int V_1_0_0_Beta2_ID = 1000002;
public final boolean enabled;
public Plugin() {
if (Version.CURRENT.id < V_0_90_8_ID) {
logger.warn("Elasticsearch version [{}] is too old. Marvel is disabled (requires version 0.90.8 or higher)", Version.CURRENT);
if (Version.CURRENT.id == V_1_0_0_Beta1_ID || Version.CURRENT.id == V_1_0_0_Beta2_ID) {
logger.warn("Elasticsearch version [{}] is incompatible with Marvel. Please upgrade to version 1.0.0.RC1 or higher.", Version.CURRENT);
enabled = false;
} else if (Version.CURRENT.id < V_0_90_9_ID) {
logger.warn("Elasticsearch version [{}] is too old. Marvel is disabled (requires version 0.90.9 or higher).", Version.CURRENT);
enabled = false;
} else {
enabled = true;

View file

@ -44,6 +44,8 @@ public class Utils {
builder.field("host", inetAddress.getHostName());
builder.field("ip_port", inetAddress.getHostAddress() + ":" + inetSocketAddress.getPort());
}
} else if (node.address().uniqueAddressTypeId() == 2) { // local transport
builder.field("ip_port", "_" + node.address()); // will end up being "_local[ID]"
}
if (!node.attributes().isEmpty()) {

View file

@ -27,6 +27,7 @@ import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
import org.elasticsearch.action.admin.indices.stats.ShardStats;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.common.Base64;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.io.Streams;
@ -119,7 +120,7 @@ public class ESExporter extends AbstractLifecycleComponent<ESExporter> implement
clusterStatsRenderer = new ClusterStatsRenderer();
eventsRenderer = new EventsRenderer();
logger.debug("Initialized with targets: {}, index prefix [{}], index time format [{}]", hosts, indexPrefix, indexTimeFormat);
logger.debug("initialized with targets: {}, index prefix [{}], index time format [{}]", hosts, indexPrefix, indexTimeFormat);
}
@Override
@ -145,7 +146,6 @@ public class ESExporter extends AbstractLifecycleComponent<ESExporter> implement
Map<String, IndexStats> perIndexStats = indicesStats.getIndices();
indexStatsRenderer.reset(perIndexStats.values().toArray(new IndexStats[perIndexStats.size()]));
indicesStatsRenderer.reset(indicesStats.getTotal(), indicesStats.getPrimaries());
logger.debug("exporting index_stats + indices_stats");
HttpURLConnection conn = openExportingConnection();
if (conn == null) {
return;
@ -289,23 +289,27 @@ public class ESExporter extends AbstractLifecycleComponent<ESExporter> implement
}
private HttpURLConnection openConnection(String method, String uri) {
return openConnection(method, uri, null);
private HttpURLConnection openConnection(String method, String path) {
return openConnection(method, path, null);
}
private HttpURLConnection openConnection(String method, String uri, String contentType) {
private HttpURLConnection openConnection(String method, String path, String contentType) {
int hostIndex = 0;
try {
for (; hostIndex < hosts.length; hostIndex++) {
String host = hosts[hostIndex];
try {
URL templateUrl = new URL("http://" + host + "/" + uri);
HttpURLConnection conn = (HttpURLConnection) templateUrl.openConnection();
URL url = new URL("http://" + host + "/" + path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(method);
conn.setConnectTimeout(timeout);
if (contentType != null) {
conn.setRequestProperty("Content-Type", XContentType.SMILE.restContentType());
}
if (url.getUserInfo() != null) {
String basicAuth = "Basic " + Base64.encodeBytes(url.getUserInfo().getBytes("ISO-8859-1"));
conn.setRequestProperty("Authorization", basicAuth);
}
conn.setUseCaches(false);
if (method.equalsIgnoreCase("POST") || method.equalsIgnoreCase("PUT")) {
conn.setDoOutput(true);
@ -427,7 +431,7 @@ public class ESExporter extends AbstractLifecycleComponent<ESExporter> implement
HttpURLConnection conn = openConnection("HEAD", path);
if (conn == null) {
logger.error("Could not connect to any configured elasticsearch instances: [{}]", hosts);
logger.error("could not connect to any configured elasticsearch instances: [{}]", hosts);
return false;
}
@ -727,7 +731,6 @@ public class ESExporter extends AbstractLifecycleComponent<ESExporter> implement
if (closed) {
return;
}
logger.trace("pinging target es");
HttpURLConnection conn = openConnection("GET", "");
if (conn != null) {
conn.getInputStream().close(); // close and release to connection pool.

View file

@ -11,6 +11,7 @@
}
}
},
"mapper.dynamic": true,
"marvel.index_format": 1
},
"mappings": {
@ -41,57 +42,156 @@
},
"node_stats": {
"properties": {
"indices.percolate.size_in_bytes": {
"type": "long"
"fs": {
"properties": {
"total": {
"properties": {
"disk_io_op": {
"type": "long"
},
"disk_reads": {
"type": "long"
},
"disk_writes": {
"type": "long"
},
"disk_io_size_in_bytes": {
"type": "long"
},
"disk_read_size_in_bytes": {
"type": "long"
},
"disk_write_size_in_bytes": {
"type": "long"
}
}
}
}
},
"jvm.buffer_pools.direct.used_in_bytes": {
"type": "long"
"indices": {
"properties": {
"percolate": {
"properties": {
"size_in_bytes": {
"type": "long"
}
}
}
}
},
"jvm.buffer_pools.mapped.used_in_bytes": {
"type": "long"
"jvm": {
"properties": {
"buffer_pools": {
"properties": {
"direct": {
"properties": {
"used_in_bytes": {
"type": "long"
}
}
},
"mapped": {
"properties": {
"used_in_bytes": {
"type": "long"
}
}
}
}
},
"gc": {
"properties": {
"collectors": {
"properties": {
"young": {
"properties": {
"collection_count": {
"type": "long"
},
"collection_time_in_millis": {
"type": "long"
}
}
},
"old": {
"properties": {
"collection_count": {
"type": "long"
},
"collection_time_in_millis": {
"type": "long"
}
}
}
}
}
}
}
}
},
"jvm.gc.collectors.young.collection_count": {
"type": "long"
"indices": {
"properties": {
"percolate": {
"properties": {
"total": {
"type": "long"
},
"time_in_millis": {
"type": "long"
},
"queries": {
"type": "long"
},
"memory_size_in_bytes": {
"type": "long"
}
}
}
}
},
"jvm.gc.collectors.young.collection_time_in_millis": {
"type": "long"
},
"jvm.gc.collectors.old.collection_count": {
"type": "long"
},
"jvm.gc.collectors.old.collection_time_in_millis": {
"type": "long"
},
"indices.percolate.total": {
"type": "long"
},
"indices.percolate.time_in_millis": {
"type": "long"
},
"indices.percolate.queries": {
"type": "long"
},
"indices.percolate.memory_size_in_bytes": {
"type": "long"
"os": {
"properties": {
"load_average": {
"properties": {
"1m": {
"type": "float"
},
"5m": {
"type": "float"
},
"15m": {
"type": "float"
}
}
}
}
}
}
},
"index_stats": {
"properties": {
"total.percolate.memory_size_in_bytes": {
"type": "long"
},
"total.percolate.time_in_millis": {
"type": "long"
},
"total.percolate.queries": {
"type": "long"
},
"total.percolate.total": {
"type": "long"
"total": {
"properties": {
"percolate": {
"properties": {
"total": {
"type": "long"
},
"time_in_millis": {
"type": "long"
},
"queries": {
"type": "long"
},
"memory_size_in_bytes": {
"type": "long"
}
}
}
}
}
}
}
}
}

11
docs/changes.asciidoc Normal file
View file

@ -0,0 +1,11 @@
[[change_list]]
== Change list
=== 1.0.2
- Kibana uses `window.location.protocol` (http or https) to make ES calls.
- Added support for basic authentication when sending data from agent. See <<configuration>>.
- Reduced DEBUG logging verbosity.
=== 1.0.1
- fixed an issue with usage statistics report.
- improve logging message when running on old Elasticsearch versions.

112
docs/configuration.asciidoc Normal file
View file

@ -0,0 +1,112 @@
[[configuration]]
== Configuration Options
[[stats-export]]
=== Statistics exporting
`marvel.agent.exporter.es.hosts`::
A list of hosts in `hostname:port` format to which statistics and events will
be sent. Data will be sent to the first host, but will failover to the next
host(s) if the first is not reachable. Defaults to `["localhost:9200"]`.
+
added[1.0.2] - HTTP Basic authentication credentials can be specified as part of the host name,
i.e., ["user:pwd@host:9200"]
`marvel.agent.enabled`::
Set this to `false` to disable all exporting of data. Set this on the
monitoring cluster, which is used to receive and analyze data from production,
to avoid collecting data from the monitoring cluster itself.
`marvel.agent.indices`::
Controls which indices to export data for. Uses simple `test1,test2,test3`
notation (or `_all` for all indices). It also supports wildcards, for
example: `test*`, and the ability to "add" (`+`) and "remove" (`-`), for
example: `+test*,-test3`. Defaults to `*`
`marvel.agent.exporter.es.index.timeformat`::
Controls the time component in the index name to which data is exported.
Defaults to `"YYYY.MM.dd"`, which produces index names like:
`.marvel-2014.01.28`. Supports date formats as explained
http://joda-time.sourceforge.net/api-release/org/joda/time/format/DateTimeFormat.html[here].
[[marvel-indices]]
=== Marvel indices
Marvel stores it's data using time based indices. By default, Marvel generates
an index per day, with one shard and one replica. We expect this to be a good
default which will fit most use cases. You may need to change it for very big
installations.
[[config-marvel-indices]]
==== Configuring Marvel's indices
Marvel uses an {ref}/indices-templates.html[index template] to preconfigure newly created indices. You can retrieve it with:
[source,sh]
----------------------------------
GET /_template/marvel
----------------------------------
To change the default settings, you may add your own template to override it. Make sure your template uses
the `.marvel-*` matching pattern and has an order of 1 or higher. For example, this template will increase the
number of shards to 5:
[source,json]
----------------------------------
PUT /_template/custom_marvel
{
"template": ".marvel*",
"order": 1,
"settings": {
"number_of_shards": 5
}
}
----------------------------------
IMPORTANT: We recommend only changing the `settings` section. Other sections are
important for the correct operation of the dashboards.
For reference, here is the `settings` section of the default template:
[source,json]
----------------------------------
{
"template": ".marvel*",
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"analysis": {
"analyzer": {
"default": {
"type": "standard",
"stopwords": "_none_"
}
}
},
"mapper.dynamic": true,
"marvel.index_format": 1
}
.....
}
----------------------------------
[[relevant-settings]]
=== Other relevant Elasticsearch settings
Marvel relies on Elasticsearch's capability to automatically create new indices
when indexing documents. Some people like to disable this feature to better
protect against mistakes in production clusters. To do so but still allow the
creation of Marvel indices you need to set the following:
[source,yaml]
----------------------
action.auto_create_index: .marvel-*
----------------------
More information is available {ref}/docs-index_.html#index-creation[here]

52
docs/dashboards.asciidoc Normal file
View file

@ -0,0 +1,52 @@
== Marvel's Dashboards
=== Overview Dashboard
The _Overview_ dashboard is Marvel's main page. The dashboard displays the
essentials metrics you need to know that your cluster is healthy. It also
provides an overview of your nodes and indices, displayed in two clean tables
with the relevant key metrics. If some value needs your attention, they will
be highlighted in yellow or red. The nodes and indices tables also serve as an
entry point to the more detailed _Node Statistics_ and _Index Statistics_
dashboards.
image:images/overview_thumb.png["Overview Dashboard",link="images/overview.png"]
=== Node & Index Statistics
The _Node Statistics_ dashboard displays metric charts from the perspective of
one or more nodes. Metrics include hardware level metrics (like load and CPU
usage), process and JVM metrics (memory usage, GC), and node level
Elasticsearch metrics such as field data usage, search requests rate and
thread pool rejection.
image:images/node_stats_thumb.png["Node Statistics Dashboard",link="images/node_stats.png"]
The _Index Statistics_ dashboard is very similar to the _Node Statistics_
dashboard, but it shows you all the metrics from the perspective of one or
more indices. The metrics are per index, with data aggregated from all of the
nodes in the cluster. For example, the ''store size'' chart shows the total
size of the index data across the whole cluster.
image:images/index_stats_thumb.png["Index Statistics Dashboard",link="images/index_stats.png"]
=== Cluster Pulse
The Cluster Pulse Dashboard allows you to see any event of interest in the cluster. Typical
events include nodes joining or leaving, master election, index creation, shard (re)allocation
and more.
image:images/cluster_pulse_thumb.png["Index Statistics Dashboard",link="images/cluster_pulse.png"]
=== Sense
_Sense_ is a lightweight developer console. The console is handy when you want
to make an extra API call to check something or perhaps tweak a setting. The
developer console understands both JSON and the Elasticsearch API, offering
suggestions and autocompletion. It is very useful for prototyping queries,
researching your data or any other administrative work with the API.
image::images/sense_thumb.png["Developer Console",link="sense.png"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
docs/images/index_stats.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
docs/images/node_stats.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
docs/images/overview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
docs/images/sense.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

BIN
docs/images/sense_thumb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

26
docs/index.asciidoc Normal file
View file

@ -0,0 +1,26 @@
= Marvel Documentation
:ref: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current
Marvel is a management and monitoring product for Elasticsearch. Marvel
aggregates cluster wide statistics and events and offers a single interface to
view and analyze them. Marvel is free for development use but does require a
license to run in production. For more details, please see the
http://www.elasticsearch.com/marvel[Marvel product page].
include::dashboards.asciidoc[]
== System Requirements
[horizontal]
Elasticsearch:: 0.90.9 or above
Browser:: The latest version of Chrome, Firefox or Safari is
recommended. Internet Explorer 9 and above is also supported.
include::install.asciidoc[]
include::configuration.asciidoc[]
include::changes.asciidoc[]

127
docs/install.asciidoc Normal file
View file

@ -0,0 +1,127 @@
== Installation
Marvel is installed as an Elasticsearch plugin. The plugin must be installed
on *every node in the cluster*. By default, the plugin will store data in the
same Elasticsearch cluster that it is monitoring.
[TIP]
====
If you are monitoring a production cluster we recommend that you send this
data to another cluster that will serve as a _monitoring cluster_.
This will help to prevent a problem in one cluster from impacting the other
cluster. For example, when your production cluster is having trouble you would
like to make sure you can view the Marvel data easily.
====
=== Simple install
This is simplest way to install and use Marvel. Marvel data will be stored on
the same cluster as the one being monitored and you will be able to access the
Marvel UI from every node. Follow these steps on every node in the cluster:
1. From the Elasticsearch home directory, run:
+
[source,sh]
----------------
bin/plugin -i elasticsearch/marvel/latest
----------------
2. Restart your Elasticsearch node
Once the plugin is installed on all nodes, you can access the Marvel UI by
viewing http://any-server-in-cluster:9200/_plugin/marvel/ with any modern
browser. It may take a minute for the initial data to appear.
A couple of points to consider:
* Since this requires to restart the nodes of your cluster, you might want
to temporarily {ref}/modules-cluster.html[disable allocations].
* If you run your cluster using a non-standard port, you need to change the
`marvel.agent.exporter.es.hosts` settings. See <<configuration>>.
* If your server doesn't have direct internet access see <<manual_download>>
for an alternative way to get the Marvel binaries.
=== Installing a secondary Monitoring Cluster
First, prepare the _monitoring cluster_ -- the cluster you will use to store
and analyze Marvel's data. For this example, we will assume two hosts,
`es-mon-1` and `es-mon-2`, are in the monitoring cluster.
==== The monitoring cluster
For each node in the monitoring cluster:
1. Prevent the nodes in the monitoring cluster from generating their own stats,
by adding the following line to `config/elasticsearch.yml`:
+
[source,yaml]
------------------------
marvel.agent.enabled: false
------------------------
2. Install the plugin. From the Elasticsearch home directory run
+
[source,sh]
----------------
bin/plugin -i elasticsearch/marvel/latest
----------------
3. Restart Elasticsearch
==== The production cluster
1. Tell each node where to send its stats by adding this line to
`config/elasticsearch.yml:
+
[source,yaml]
------------------------
marvel.agent.exporter.es.hosts: ["es-mon-1:9200","es-mon-2:9200"]
------------------------
+
This line adds two hosts of your monitoring cluster to receive the data, a
primary and a backup.
2. Install the plugin. From the Elasticsearch home directory run
+
[source,sh]
------------------------
bin/plugin -i elasticsearch/marvel/latest
------------------------
3. Restart Elasticsearch
Once the plugin is installed on all nodes, you can access the Marvel UI by
opening this link with any modern browser.
http://any-server-in-monitoring-cluster:9200/_plugin/marvel/
It may take a minute or two for data to appear.
TIP: You may want to temporarily {ref}/modules-cluster.html[disable shard
allocation] before you restart your nodes to avoid unnecessary shard
reallocation during the install process.
[[manual_download]]
==== Manual download
Elasticsearch's `bin/plugin` script requires direct internet access for downloading
and installing Marvel. If your server doesn't have internet access, you can download
the Marvel binaries from the following link: https://download.elasticsearch.org/elasticsearch/marvel/marvel-latest.zip
Once you have transferred the zip file to your server, you can use the `bin/plugin`
script to install it:
[source,sh]
----------------
bin/plugin -i marvel -u file://PATH_TO_MARVEL_ZIP_FILE
----------------
After the script has finish, continue according to the standard installation procedure.

View file

@ -21,7 +21,7 @@ define(['settings'],
* kibana installed on. You probably want to set it to the FQDN of your
* elasticsearch host
*/
elasticsearch: "http://"+window.location.hostname+@@port,
elasticsearch: window.location.protocol+"//"+window.location.hostname+@@port,
/** @scratch /configuration/config.js/5
* ==== default_route

View file

@ -56,7 +56,8 @@
}
},
"rows": [
{ "title": "Cluster Summary",
{
"title": "Cluster Summary",
"height": "75px",
"editable": true,
"panels": [
@ -173,6 +174,8 @@
"trimFactor": 300,
"normTimes": true,
"spyable": true,
"localTime": true,
"timeField": "@timestamp",
"title": "Cluster events"
},
{

View file

@ -66,13 +66,8 @@ if (!_.isUndefined(ARGS.queries)) {
}];
}));
} else {
// No queries passed? Initialize a single query to match everything
queries = {
0: {
query: '*',
id: 0
}
};
// No queries passed
queries = {};
}
var show = (ARGS.show || "").split(',');
@ -129,7 +124,7 @@ panel_defaults_by_type["histogram"] = {
legend_counts: false,
options: false,
legend: false,
resolution: 20,
resolution: 22,
y_format: "short"
};
@ -395,11 +390,11 @@ dashboard.rows = _.map(rows, function (r) {
return r;
});
if (!showedSomething && dashboard.rows.length > 0) {
// open the first row if nothing was opened and we have queries (o.w. it's meaningless)
if (!showedSomething && dashboard.rows.length > 0 && _.size(queries) > 0) {
dashboard.rows[0].collapse = false;
}
dashboard.pulldowns = [
{
"type": "query",

View file

@ -68,13 +68,8 @@ if (!_.isUndefined(ARGS.queries)) {
}));
marker_query = "(" + _.pluck(queries, "query").join(") OR (") + ")";
} else {
// No queries passed? Initialize a single query to match everything
queries = {
0: {
query: '*',
id: 0
}
};
// No queries passed
queries = {};
}
var annotate_config;
@ -148,7 +143,7 @@ panel_defaults_by_type["histogram"] = {
zoomlinks: false,
options: false,
legend: false,
resolution: 20,
resolution: 22,
annotate: annotate_config,
y_format: "short"
};
@ -690,7 +685,8 @@ dashboard.rows = _.map(rows, function (r) {
return r;
});
if (!showedSomething && dashboard.rows.length > 0) {
// open the first row if nothing was opened and we have queries (o.w. it's meaningless)
if (!showedSomething && dashboard.rows.length > 0 && _.size(queries) > 0) {
dashboard.rows[0].collapse = false;
}

View file

@ -145,7 +145,7 @@
"pointradius": 5,
"annotate": {
"enable": true,
"query": "_type:cluster_event",
"query": "_type:cluster_event OR _type:node_event",
"size": 50,
"field": "message",
"sort": [
@ -218,7 +218,7 @@
"pointradius": 5,
"annotate": {
"enable": true,
"query": "_type:cluster_event",
"query": "_type:cluster_event OR _type:node_event",
"size": 50,
"field": "message",
"sort": [
@ -291,7 +291,7 @@
"pointradius": 5,
"annotate": {
"enable": true,
"query": "_type:cluster_event",
"query": "_type:cluster_event OR _type:node_event",
"size": 50,
"field": "message",
"sort": [

View file

@ -15,11 +15,11 @@
<div class="section">
<div class="editor-option">
<label class="small">Send statistics<tip>Help make Elasticsearch better by sharing anonymous usage data.</tip></label>
<input type="checkbox" ng-model="cookies.marvelOptIn" ng-true-value="IN" ng-false-value="OUT" />
<input type="checkbox" ng-model="marvelOpts.report"/>
</div>
<div ng-show="kbnVersion == '@REV@'" class="editor-option">
<label class="small">Clear Marvel Cookies <tip>Only shown in unbuilt versions, clear optin and version cookies</tip></label>
<button class="btn btn-danger" ng-click="clearMarvelCookies()">Clear</button>
<label class="small">Clear marvelOpts <tip>Only shown in unbuilt versions, clear stored optin and version data</tip></label>
<button class="btn btn-danger" ng-click="clearMarvelStorage()">Clear</button>
</div>
</div>
</div>

View file

@ -12,17 +12,18 @@
define([
'angular',
'app',
'kbn',
'lodash',
'kbn'
'factories/store'
],
function (angular, app, _, kbn) {
function (angular, app, kbn, _) {
'use strict';
var module = angular.module('kibana.panels.marvel.cluster', []);
app.useModule(module);
module.controller('marvel.cluster', function($scope, $modal, $q, $cookies, $http,
querySrv, dashboard, filterSrv, kbnVersion) {
module.controller('marvel.cluster', function($scope, $modal, $q, $http,
querySrv, dashboard, filterSrv, kbnVersion, storeFactory) {
$scope.panelMeta = {
modals : [],
editorTabs : [],
@ -37,15 +38,19 @@ function (angular, app, _, kbn) {
_.defaults($scope.panel,_d);
var reportInterval = 86400000;
//var reportInterval = 30000;
// setup the optIn and version values
var marvelOpts = storeFactory($scope, 'marvelOpts', {
report: void 0,
version: void 0,
lastReport: void 0
});
$scope.init = function () {
// So we can access the cookies object from the view
$scope.cookies = $cookies;
$scope.kbnVersion = kbnVersion;
// If the user hasn't opted in or out, ask them to.
if(_.isUndefined($cookies.marvelOptIn) || $cookies.marvelVersion !== kbnVersion) {
if(marvelOpts.version == null || marvelOpts.version !== kbnVersion) {
$scope.optInModal();
}
@ -129,15 +134,15 @@ function (angular, app, _, kbn) {
$scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
};
$scope.setOptIn = function(c) {
$cookies.marvelVersion = kbnVersion;
$cookies.marvelOptIn = c;
$scope.setOptIn = function(val) {
marvelOpts.version = kbnVersion;
marvelOpts.report = val;
};
$scope.clearMarvelCookies = function() {
delete $cookies.marvelOptIn;
delete $cookies.marvelVersion;
delete $cookies.marvelLastReport;
$scope.clearMarvelStorage = function() {
marvelOpts.report = void 0;
marvelOpts.version = void 0;
marvelOpts.lastReport = void 0;
};
$scope.optInModal = function() {
@ -157,10 +162,10 @@ function (angular, app, _, kbn) {
// Checks if we should send a report
var checkReport = function() {
if($cookies.marvelOptIn === 'IN') {
if(_.isUndefined($cookies.marvelLastReport)) {
if(marvelOpts.report) {
if(marvelOpts.lastReport == null) {
return true;
} else if (new Date().getTime() - parseInt($cookies.marvelLastReport,10) > reportInterval) {
} else if (new Date().getTime() - parseInt(marvelOpts.lastReport,10) > reportInterval) {
return true;
} else {
return false;
@ -182,7 +187,7 @@ function (angular, app, _, kbn) {
$scope.config.stats_report_url,
data
).success(function() {
$cookies.marvelLastReport = thisReport;
marvelOpts.lastReport = thisReport;
});
};

View file

@ -26,8 +26,7 @@
</div>
<div class="modal-footer">
<!-- these set strings since cookies don't deal with boolean -->
<button ng-click="setOptIn('IN');dismiss();" class="btn btn-success">Count me in!</button>
<span ng-click="setOptIn('OUT');dismiss();" class="pointer small">No Thanks</span>
<button ng-click="setOptIn(true);dismiss();" class="btn btn-success">Count me in!</button>
<span ng-click="setOptIn(false);dismiss();" class="pointer small">No Thanks</span>
</div>

View file

@ -1,9 +1,4 @@
<div ng-controller='marvel.navigation' ng-init="init()">
<style>
.marvel-navigation-dropdown {
margin: 0px !important;
}
</style>
<!-- This is a complete hack. The form actually exists in the modal, but due to transclusion
$scope.input isn't available on the controller unless the form element is in this file -->
<form name="input" style="margin:3px 0 0 0">

View file

@ -14,10 +14,10 @@
define([
'angular',
'app',
'lodash',
'jquery'
'jquery',
'lodash'
],
function (angular, app, _, $) {
function (angular, app, $, _) {
'use strict';
var module = angular.module('kibana.panels.marvel.navigation', []);
@ -75,7 +75,10 @@ function (angular, app, _, $) {
$scope.links = _.filter(response.data.links, function (link) {
a.attr("href", link.url);
return a[0].href !== window.location.href;
var current = window.location.href;
// remove parameters
current = current.replace(/\?.*$/,'');
return a[0].href !== current;
});
});
}

View file

@ -32,10 +32,11 @@
height: 10px;
width: 50px;
}
</style>
<div class="pull-left marvel-header marvel-table" ng-show="rows.length > 0">
<input type="text" class="input-medium" placeholder="Filter {{panel.mode}}..." ng-model="panel.rowFilter"> <span class="count">{{(rows|filter:panel.rowFilter|limitTo:rowLimit).length}} of {{rows.length}} {{panel.mode}}</span> / {{(rows|filter:{'selected':true}).length}} selected / Last 10m </span>
<div class="pull-left marvel-header marvel-table" ng-show="rows.length > 0 || panel.rowFilter">
<input type="text" class="input-medium" placeholder="Filter {{panel.mode}}..." ng-model="panel.rowFilter" ng-change="onRowFilterChange()"> <span class="count">{{rows.length}} of {{_.size(data)}} {{panel.mode}}</span> / {{selectedData().length}} selected / Last 10m </span>
<br>
<span class="small muted pull-right" ng-show="!viewSelect"> <i class="icon-warning"></i> Compact view. Filter {{panel.mode}} to 5 or less, or set the page refresh rate to 2m or greater for more options.</span>
</div>
@ -50,34 +51,34 @@
<th class="pointer" ng-click="set_sort('__name__')">
{{panel.mode}}
<i ng-show="'__name__' == panel.sort[0]" class="pointer link" ng-class="{'icon-chevron-up': panel.sort[1] == 'asc','icon-chevron-down': panel.sort[1] == 'desc'}"></i>
<a ng-disabled="!hasSelected(rows)" id="detail_view_link" ng-href="{{detailViewLink()}}" class="btn btn-mini btn-info pull-right" bs-tooltip="detailViewTip()" data-placement="right">Dashboard</a>
<a ng-disabled="!hasSelected()" id="detail_view_link" ng-href="{{detailViewLink()}}" class="btn btn-mini btn-info pull-right" bs-tooltip="detailViewTip()" data-placement="right">Dashboard</a>
</th>
<th ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels['_global_'][metric.field])" class="pointer" ng-click="set_sort(metric.field)">
<th ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels[metric.field])" class="pointer" ng-click="set_sort(metric.field)">
{{metric.name}}
<i ng-show='metric.field == panel.sort[0]' class="pointer link" ng-class="{'icon-chevron-up': panel.sort[1] == 'asc','icon-chevron-down': panel.sort[1] == 'desc'}"></i>
</th>
</thead>
<tr ng-repeat="row in rows | filter:panel.rowFilter | orderBy:get_sort_value:panel.sort[1]=='desc'|limitTo:rowLimit">
<tr ng-repeat="row in rows" >
<td>
<div class="checkbox">
<label>
<div>
<label class="checkbox" ng-class="{'text-warning':!row.alive}">
<input type="checkbox" ng-model="row.selected" ng-checked="row.selected">
<span class="pointer" ng-click="rowClick(row)">{{row.display_name}}</span>
<div class="marvel-persistent-name pointer" ng-hide="row.id == row.display_name" ng-click="rowClick(row)">{{row.id}} <i bs-tooltip="'No report has been received for more than '+staleSeconds+' seconds'" ng-show="!isCurrent(data[row.id+'_@timestamp'].max,staleSeconds*1000)" class="icon-exclamation-sign"></i></div>
<span class="pointer" ng-click="rowClick(row)">{{row.display_name}} <i bs-tooltip="'No report has been received for more than '+staleSeconds+' seconds'" ng-show="!row.alive" class="icon-exclamation-sign"></i></span>
<div class="marvel-persistent-name pointer" ng-hide="row.id == row.display_name" ng-click="rowClick(row)">{{row.id}}</div>
</label>
</div>
</td>
<td ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels[row.id][metric.field])">
<td ng-repeat="metric in panel.metrics" ng-class="alertClass(row[metric.field].alert_level)">
<div class="marvel-mean pointer" ng-click="rowClick(row,metric)">
<span bo-text="(_.isNull(data[row.id+'_'+metric.field].mean)?0:data[row.id+'_'+metric.field].mean) | metric_format:metric"></span>
<span bo-text="(_.isNull(row[metric.field].mean)?'n/a':row[metric.field].mean) | metric_format:metric"></span>
<br>
<div class="marvel-stats-sparkline" panel='panel' field="metric.field" series="data[row.id+'_'+metric.field+'_history'].series"></div>
<div class="marvel-stats-sparkline" panel='panel' field="metric.field" series="row[metric.field].series"></div>
</div>
<div class="marvel-extended pointer" ng-click="rowClick(row,metric)">
<span>min: <span bo-text="(_.isNull(data[row.id+'_'+metric.field].min)?0:data[row.id+'_'+metric.field].min) | metric_format:metric"></span></span><br>
<span>max: <span bo-text="(_.isNull(data[row.id+'_'+metric.field].max)?0:data[row.id+'_'+metric.field].max) | metric_format:metric"></span></span>
<span>min: <span bo-text="(_.isNull(row[metric.field].min)?'n/a':row[metric.field].min) | metric_format:metric"></span></span><br>
<span>max: <span bo-text="(_.isNull(row[metric.field].max)?'n/a':row[metric.field].max) | metric_format:metric"></span></span>
</div>
</td>
</tr>
@ -89,15 +90,15 @@
<th class="pointer" ng-click="set_sort('__name__')">
{{panel.mode}}
<i ng-show="'__name__' == panel.sort[0]" class="pointer link" ng-class="{'icon-chevron-up': panel.sort[1] == 'asc','icon-chevron-down': panel.sort[1] == 'desc'}"></i>
<a ng-disabled="!hasSelected(rows)" id="detail_view_link" ng-href="{{detailViewLink()}}" class="btn btn-mini btn-info pull-right" bs-tooltip="detailViewTip()" data-placement="right">Dashboard</a>
<a ng-disabled="!hasSelected()" id="detail_view_link" ng-href="{{detailViewLink()}}" class="btn btn-mini btn-info pull-right" bs-tooltip="detailViewTip()" data-placement="right">Dashboard</a>
</th>
<th ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels['_global_'][metric.field])" class="pointer" ng-click="set_sort(metric.field)">
<th ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels[metric.field])" class="pointer" ng-click="set_sort(metric.field)">
{{metric.name}}
<i ng-show='metric.field == panel.sort[0]' class="pointer link" ng-class="{'icon-chevron-up': panel.sort[1] == 'asc','icon-chevron-down': panel.sort[1] == 'desc'}"></i>
</th>
</thead>
<tr ng-repeat="row in rows | filter:panel.rowFilter | orderBy:get_sort_value:panel.sort[1]=='desc'|limitTo:rowLimit">
<tr ng-repeat="row in rows">
<td>
<div class="checkbox">
<label>
@ -106,11 +107,11 @@
<span class="pointer" ng-click="rowClick(row)">{{ row.id }}</span>
</div>
</td>
<td ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels[row.id][metric.field])">
<td ng-repeat="metric in panel.metrics" ng-class="alertClass(row[metric.field].alert_level)">
<div class="pointer" ng-click="rowClick(row,metric)">
<span bo-text="(_.isNull(data[row.id+'_'+metric.field].mean)?0:data[row.id+'_'+metric.field].mean) | metric_format:metric"></span>
<span bo-text="(_.isNull(row[metric.field].mean)?'n/a':row[metric.field].mean) | metric_format:metric"></span>
<div ng-if="sparkLines" class="marvel-stats-sparkline pointer" ng-click="rowClick(row,metric)" panel='panel' field="metric.field" series="data[row.id+'_'+metric.field+'_history'].series"></div>
<div ng-if="sparkLines" class="marvel-stats-sparkline pointer" ng-click="rowClick(row,metric)" panel='panel' field="metric.field" series="row[metric.field].series"></div>
</div>
</td>
</tr>

View file

@ -18,14 +18,15 @@ define([
log_debug = function (msg) {
console.log(msg);
};
} else {
}
else {
log_debug = function () {
};
}
function y_format_metric_value(value, metric) {
// If this isn't a number, change nothing
if (_.isNaN(value) || !_.isFinite(value)) {
if (_.isNaN(value) || !_.isFinite(value) || !_.isNumber(value)) {
return value;
}
if (metric.y_format === 'bytes') {
@ -53,7 +54,14 @@ define([
var _d = {
compact: false,
mode: 'nodes',
sort: ['__name__', 'asc']
sort: null,
// if you have more rows than this number, full view will be disabled
full_view_row_limit_on_high_refresh: 5,
// if you have more nodes/indices than this number, refresh rate will be capped at 2,
data_limit_for_high_refresh: 50,
// disable display names if more than this number of nodes/indices.
data_limit_for_display_names: 50
//
};
_.defaults($scope.panel, _d);
@ -64,7 +72,7 @@ define([
add: undefined
};
$scope.staleSeconds = 60;
$scope.staleSeconds = 20;
$scope.modeInfo = {
nodes: {
@ -92,8 +100,8 @@ define([
{
name: 'JVM Mem (%)',
field: 'jvm.mem.heap_used_percent',
warning: 95,
error: 98
warning: 90,
error: 95
},
{
name: 'Disk Free Space',
@ -150,7 +158,7 @@ define([
y_format: "bytes"
},
{
name: 'Field data',
name: 'Field Data',
field: 'total.fielddata.memory_size_in_bytes',
y_format: "bytes"
},
@ -193,8 +201,8 @@ define([
return metricDefaults(m);
});
$scope.$watch('panel.mode', function (m) {
if (_.isUndefined(m)) {
$scope.$watch('panel.mode', function (m, prev) {
if (m === prev || _.isUndefined(m)) {
return;
}
$scope.panel.display_field = $scope.modeInfo[m].defaults.display_field;
@ -204,32 +212,31 @@ define([
});
});
$scope.$watch('(rows|filter:panel.rowFilter).length', function (l) {
//Compute view based on number of rows
rowsVsRefresh(l);
});
$scope.$watch('dashboard.current.refresh', function () {
var l = $filter('filter')($scope.rows, $scope.panel.rowFilter).length;
rowsVsRefresh(l);
$scope.updateUIFeaturesBasedOnData();
});
var rowsVsRefresh = function (l) {
if (l > 5 && kbn.interval_to_seconds(dashboard.current.refresh || '1y') < 120) {
$scope.updateUIFeaturesBasedOnData = function () {
var l = $scope.rows.length;
if (l > $scope.panel.full_view_row_limit_on_high_refresh
&& kbn.interval_to_seconds(dashboard.current.refresh || '1y') < 120) {
$scope.panel.compact = true;
$scope.sparkLines = true;
$scope.viewSelect = false;
if(l > 50 && kbn.interval_to_seconds(dashboard.current.refresh || '1y') < 120) {
dashboard.set_interval('2m');
alertSrv.set('Refresh rate',
'Due to the large size of your cluster, the refresh rate has been adjusted to 2m',
'info',30000);
}
} else {
}
else {
$scope.viewSelect = true;
$scope.sparkLines = true;
}
if (_.size($scope.data) > $scope.panel.data_limit_for_high_refresh
&& kbn.interval_to_seconds(dashboard.current.refresh || '1y') < 120) {
dashboard.set_interval('2m');
alertSrv.set('Refresh rate',
'Due to the large size of your cluster, the refresh rate has been adjusted to 2m',
'info', 30000);
}
};
$scope.init = function () {
@ -241,20 +248,28 @@ define([
$scope.warnLevels = {};
$scope.rows = [];
$scope.data = {};
$scope.$on('refresh', function () {
$scope.get_rows();
$scope.get_data();
});
$scope.get_rows();
$scope.get_data();
};
$scope.get_mode_filter = function () {
return $scope.ejs.TermFilter("_type", $scope.panel.mode === "nodes" ? "node_stats" : "index_stats");
};
$scope.get_summary_key = function (id, m) {
return id + "_" + m.field;
};
$scope.get_history_key = function (id, m) {
return id + "_" + m.field + "_history";
};
/*
marks the start of data retrieval. returns true if retrieval should continue
or false if not. May schedule future retrival if needed.
or false if not. May schedule future retrieval if needed.
*/
$scope._register_data_start = function () {
@ -281,7 +296,7 @@ define([
// somehow this was not picked up... retry
console.log("Retrying call from " + now);
$scope._pending_data_retrieval = null;
$scope.get_rows();
$scope.get_data();
}
}, 20000);
}
@ -301,16 +316,16 @@ define([
}
log_debug("firing pending retrieval " + $scope._pending_data_retrieval);
$scope._pending_data_retrieval = null;
$scope.get_rows();
$scope.get_data();
}, 5000); // leave 5 second of some breathing air
} else {
}
else {
$scope._ongoing_data_retrieval = null;
}
};
$scope.get_rows = function () {
$scope.get_data = function () {
if (dashboard.indices.length === 0) {
return;
}
@ -322,8 +337,7 @@ define([
var
request,
filter,
results,
facet;
results;
filter = filterSrv.getBoolFilter(filterSrv.ids);
@ -335,54 +349,122 @@ define([
filter.must($scope.get_mode_filter()).must($scope.ejs.RangeFilter('@timestamp').from(to + "-10m/m").to(to + "/m"));
request = $scope.ejs.Request().indices(dashboard.indices).size(0).searchType("count");
facet = $scope.ejs.TermsFacet('terms')
.field($scope.panel.persistent_field)
.size(9999999)
request.query($scope.ejs.FilteredQuery($scope.ejs.MatchAllQuery(), filter));
// timestamp facet to give us the proper time ranges for each node
request.facet($scope.ejs.TermStatsFacet("timestamp")
.keyField($scope.panel.persistent_field).valueField("@timestamp")
.order('term')
.facetFilter(filter);
.size(2000));
if (!$scope.panel.show_hidden) {
facet.regex("[^.].*");
}
request.facet(facet);
_.each($scope.panel.metrics, function (m) {
request.facet($scope.ejs.TermStatsFacet(m.field)
.keyField($scope.panel.persistent_field).valueField(m.field)
.order('term')
.size(2000));
});
results = request.doSearch();
results.then(function (r) {
var newPersistentIds = _.pluck(r.facets.terms.terms, 'term'),
mrequest;
var mrequest, newData;
if (newPersistentIds.length === 0) {
// populate the summary data based on the other facets
newData = {};
_.each(r.facets['timestamp'].terms, function (f) {
if (!$scope.panel.show_hidden && f.term[0] === ".") {
return;
}
newData[f.term] = {
id: f.term,
time_span: (f.max - f.min) / 1000,
alive: (new Date().getTime() - f.max > $scope.staleSeconds*1000) ? false : true,
selected: ($scope.data[f.term] || {}).selected,
alert_level: 0
};
});
_.each($scope.panel.metrics, function (m) {
_.each(r.facets[m.field].terms, function (f) {
var summary = newData[f.term];
if (!summary) {
return; // filtered
}
var m_summary = {
mean: null,
max: null,
min: null
};
if (m.derivative) {
// no min max, but we can do avg, if we have a timestamp
if (!summary.time_span) {
summary[m.field] = m_summary;
return;
}
m_summary.mean = (f.max - f.min) / summary.time_span;
if (m.scale && m.scale !== 1) {
m_summary.mean /= m.scale;
}
}
else {
m_summary.min = f.min;
m_summary.max = f.max;
m_summary.mean = f.mean;
if (m.scale && m.scale !== 1) {
m_summary.mean /= m.scale;
m_summary.max /= m.scale;
m_summary.min /= m.scale;
}
}
summary[m.field] = m_summary;
m_summary.alert_level = $scope.alertLevel(m, m_summary.mean);
// if (f.term === "index_t_200" && m.field === "primaries.docs.count") {
// m_summary.alert_level = 1;
// }
// if (f.term === "index_t_300" && m.field === "primaries.indexing.index_total") {
// m_summary.alert_level = 2;
// }
if (m_summary.alert_level > summary.alert_level) {
summary.alert_level = m_summary.alert_level;
}
});
});
if (_.isEmpty(newData)) {
// call the get data function so it will clear out all other data related objects.
$scope.get_data([]);
$scope._register_data_end();
$scope.select_display_data_and_enrich(newData);
return;
}
// in all this cases we don't need the display name, short cut it.
if (!$scope.panel.display_field || $scope.panel.display_field === $scope.panel.persistent_field ||
$scope.panel.compact
_.size(newData) > $scope.panel.data_limit_for_display_names
) {
$scope.get_data(_.map(newPersistentIds, function (id) {
return {
display_name: id,
id: id,
// using findWhere here, though its not very efficient
selected: (_.findWhere($scope.rows, {id: id}) || {}).selected
};
}));
_.each(newData, function (s) {
s.display_name = s.id;
});
$scope._register_data_end();
$scope.select_display_data_and_enrich(newData);
return;
}
// go get display names.
mrequest = $scope.ejs.MultiSearchRequest().indices(dashboard.indices);
_.each(newPersistentIds, function (persistentId) {
_.each(newData, function (s) {
var rowRequest = $scope.ejs.Request().filter(filter);
rowRequest.query(
$scope.ejs.ConstantScoreQuery().query(
$scope.ejs.TermQuery($scope.panel.persistent_field, persistentId)
$scope.ejs.TermQuery($scope.panel.persistent_field, s.id)
)
);
rowRequest.size(1).fields(_.unique([ stripRaw($scope.panel.display_field), stripRaw($scope.panel.persistent_field)]));
@ -391,10 +473,8 @@ define([
});
mrequest.doSearch(function (r) {
esVersion.is(">=1.0.0.RC1").then(function(version) {
var newRows = [],
esVersion.is('>=1.0.0.RC1').then(function(version) {
var
hit,
display_name,
persistent_name;
@ -408,90 +488,192 @@ define([
if (version) {
display_name = (hit.fields[stripRaw($scope.panel.display_field)] || [ undefined ])[0];
persistent_name = (hit.fields[stripRaw($scope.panel.persistent_field)] || [ undefined] )[0];
} else {
}
else {
display_name = hit.fields[stripRaw($scope.panel.display_field)];
persistent_name = hit.fields[stripRaw($scope.panel.persistent_field)];
}
newRows.push({
display_name: display_name || persistent_name,
id: persistent_name,
// using findWhere here, though its not very efficient
selected: (_.findWhere($scope.rows, {id: persistent_name}) || {}).selected
});
(newData[persistent_name] || {}).display_name = display_name;
});
$scope.get_data(newRows);
$scope._register_data_end();
$scope.select_display_data_and_enrich(newData);
});
}, $scope._register_data_end);
}, $scope._register_data_end);
};
$scope.get_data = function (newRows) {
function applyNewData(rows, data) {
$scope.rows = rows;
$scope.data = data;
$scope.updateUIFeaturesBasedOnData();
$scope.panelMeta.loading = false;
$scope.calculateWarnings();
}
$scope.select_display_data_and_enrich = function (newData) {
// Make sure we have everything for the request to complete
if (_.isUndefined(newRows)) {
newRows = $scope.rows;
if (_.isUndefined(newData)) {
newData = $scope.data;
}
if (dashboard.indices.length === 0 || newRows.length === 0) {
$scope.rows = newRows;
$scope.data = {};
$scope.panelMeta.loading = false;
$scope.calculateWarnings();
$scope._register_data_end();
if (dashboard.indices.length === 0 || _.isEmpty(newData)) {
applyNewData([], {});
return;
}
$scope.panelMeta.loading = true;
var
request,
results;
results,
newRows, newRowsIds;
// decide what rows we're showing...
newRowsIds = [];
if ($scope.panel.rowFilter) {
var lcFilter = $scope.panel.rowFilter.toLowerCase();
_.each(newData, function (s) {
var data = s.id.toLowerCase();
if (data.indexOf(lcFilter) >= 0) {
newRowsIds.push(s.id);
return;
}
data = s.display_name.toLowerCase();
if (data.indexOf(lcFilter) >= 0) {
newRowsIds.push(s.id);
}
});
}
else {
_.each(newData, function (s) {
newRowsIds.push(s.id);
}
);
}
function compareIdBySelection(id1, id2) {
var s1 = newData[id1], s2 = newData[id2];
if (s1.selected && !s2.selected) {
return -1;
}
if (!s1.selected && s2.selected) {
return 1;
}
return 0;
}
function compareIdByAlert(id1, id2) {
var s1 = newData[id1], s2 = newData[id2];
if (s1.alert_level > s2.alert_level) {
return -1;
}
if (s1.alert_level < s2.alert_level) {
return 1;
}
return 0;
}
function compareIdByName(id1, id2) {
var s1 = newData[id1], s2 = newData[id2];
if (s1.display_name < s2.display_name) {
return -1;
}
if (s1.display_name > s2.display_name) {
return 1;
}
return 0;
}
function compareIdByPanelSort(id1, id2) {
var v1 = $scope.get_sort_value(id1, newData),
v2 = $scope.get_sort_value(id2, newData),
r = 0;
if (v1 < v2) {
r = -1;
}
else if (v1 > v2) {
r = 1;
}
if ($scope.panel.sort[1] === "desc") {
r *= -1;
}
return r;
}
function concatSorting() {
var funcs = arguments;
return function (d1, d2) {
for (var i = 0; i < funcs.length; i++) {
var r = funcs[i].call(this, d1, d2);
if (r !== 0) {
return r;
}
}
return 0;
};
}
if ($scope.panel.sort) {
newRowsIds.sort(concatSorting(compareIdByPanelSort, compareIdByAlert, compareIdBySelection));
newRowsIds = newRowsIds.slice(0, $scope.rowLimit);
}
else {
newRowsIds.sort(concatSorting(compareIdBySelection, compareIdByAlert, compareIdByName));
newRowsIds = newRowsIds.slice(0, $scope.rowLimit);
// sort again for visual effect
// sort again for visual placement
newRowsIds.sort(concatSorting(compareIdByAlert, compareIdByName));
}
newRows = _.map(newRowsIds, function (id) {
return newData[id];
});
// now that we have selections, sort by name (if
request = $scope.ejs.Request().indices(dashboard.indices);
var to = filterSrv.timeRange(false).to;
if (to !== "now") {
to = kbn.parseDate(to).valueOf() + "||";
}
var filter = $scope.ejs.BoolFilter()
.must($scope.ejs.RangeFilter('@timestamp').from(to + "-10m/m").to(to + "/m"))
.must($scope.get_mode_filter());
request.query($scope.ejs.FilteredQuery($scope.ejs.MatchAllQuery(), filter)).size(0);
//to = kbn.parseDate(to).valueOf();
_.each(_.pluck(newRows, 'id'), function (id) {
var filter = $scope.ejs.BoolFilter()
.must($scope.ejs.RangeFilter('@timestamp').from(to + "-10m/m").to(to + "/m"))
.must($scope.ejs.TermsFilter($scope.panel.persistent_field, id))
.must($scope.get_mode_filter());
_.each(newRows, function (row) {
_.each($scope.panel.metrics, function (m) {
request = request
.facet($scope.ejs.StatisticalFacet(id + "_" + m.field)
.field(m.field)
.facetFilter(filter));
request = request.facet($scope.ejs.DateHistogramFacet(id + "_" + m.field + "_history")
if (!row[m.field] || row[m.field].series) {
// already have it or the field was not present in the first iteration. Ignore for now.
return;
}
request.facet($scope.ejs.DateHistogramFacet($scope.get_history_key(row.id, m))
.keyField('@timestamp').valueField(m.field).interval('1m')
.facetFilter(filter)).size(0);
.facetFilter($scope.ejs.TermFilter($scope.panel.persistent_field, row.id))
);
});
// Get the max of timestamp to figure out the last time the node reported
request = request
.facet($scope.ejs.StatisticalFacet(id + "_@timestamp")
.field("@timestamp")
.facetFilter(filter));
});
if (!request.facet() || request.facet().length === 0) {
applyNewData(newRows, newData);
return;
}
results = request.doSearch();
// Populate scope when we have results
results.then(function (results) {
$scope.rows = newRows;
$scope.data = normalizeFacetResults(results.facets, newRows, $scope.panel.metrics);
$scope.panelMeta.loading = false;
$scope.calculateWarnings();
$scope._register_data_end();
},
$scope._register_data_end
addHistoryFacetResults(results.facets, newRows, newData, $scope.panel.metrics);
applyNewData(newRows, newData);
}
);
};
@ -499,31 +681,30 @@ define([
return (new Date().getTime() - time > allowed) ? false : true;
};
var normalizeFacetResults = function (facets, rows, metrics) {
facets = facets || {}; // deal better with no data.
var addHistoryFacetResults = function (facets, rows, data, metrics) {
_.each(metrics, function (m) {
_.each(_.pluck(rows, 'id'), function (id) {
var summary_key = id + "_" + m.field;
var history_key = id + "_" + m.field + "_history";
var summary = facets[summary_key];
if (!summary) {
_.each(rows, function (row) {
var history_key = $scope.get_history_key(row.id, m);
var history_facet = facets[history_key];
if (!history_facet) {
// no data for this chart.
return;
}
var series_data = _.pluck(facets[history_key].entries, m.derivative ? 'min' : 'mean');
var series_time = _.pluck(facets[history_key].entries, 'time');
var series_data = _.pluck(history_facet.entries, m.derivative ? 'min' : 'mean');
var series_time = _.pluck(history_facet.entries, 'time');
var summary = row[m.field];
if (m.scale && m.scale !== 1) {
series_data = _.map(series_data, function (v) {
return v / m.scale;
});
summary.mean /= m.scale;
summary.max /= m.scale;
summary.min /= m.scale;
}
if (m.derivative) {
// update mean to match min & max. Mean is calculated using the entire period's min/max
// this can be different than the calculation here that is based of the min of every small bucket
var _l = series_data.length - 1;
if (_l <= 0) {
summary.mean = null;
@ -533,12 +714,14 @@ define([
summary.mean = (series_data[_l] - series_data[0]) / avg_time;
}
series_data = _.map(series_data, function (p, i) {
var _v;
if (i === 0) {
_v = null;
} else {
}
else {
var _t = ((series_time[i] - series_time[i - 1]) / 1000); // milliseconds -> seconds.
_v = (p - series_data[i - 1]) / _t;
}
@ -551,13 +734,16 @@ define([
summary.min = _.reduce(series_data, function (m, v) {
return m > v && v != null ? v : m;
}, Number.POSITIVE_INFINITY);
if (summary.max === Number.NEGATIVE_INFINITY) {
summary.max = null;
}
if (summary.min === Number.POSITIVE_INFINITY) {
summary.min = null;
}
}
var series = _.zip(series_time, series_data);
facets[summary_key] = summary;
facets[history_key].series = series;
summary.series = _.zip(series_time, series_data);
});
});
@ -565,32 +751,53 @@ define([
return facets;
};
$scope.hasSelected = function (nodes) {
return _.some(nodes, function (n) {
$scope.hasSelected = function (rows) {
return _.some(rows || $scope.data, function (n) {
return n.selected;
});
};
$scope.get_sort_value = function (row) {
if ($scope.panel.sort[0] === '__name__') {
return row.display_name;
$scope.selectedData = function (data) {
return _.filter(data || $scope.data, function (d) {
return d.selected;
});
};
$scope.get_sort_value = function (id, data) {
if (!data) {
data = $scope.data;
}
return $scope.data[row.id + '_' + $scope.panel.sort[0]].mean;
id = data[id];
if ($scope.panel.sort[0] === '__name__') {
return id.display_name;
}
return id[$scope.panel.sort[0]].mean;
};
$scope.set_sort = function (field) {
if ($scope.panel.sort && $scope.panel.sort[0] === field) {
$scope.panel.sort[1] = $scope.panel.sort[1] === "asc" ? "desc" : "asc";
if ($scope.panel.sort[1] === "asc") {
$scope.panel.sort[1] = "desc";
}
else if ($scope.panel.sort[1] === "desc") {
$scope.panel.sort = null;
}
else {
// shouldn't happen, but whatever
$scope.panel.sort[1] = "asc";
}
}
else {
$scope.panel.sort = [field, 'asc'];
}
$scope.select_display_data_and_enrich();
};
$scope.showFullTable = function () {
if ($scope.panel.compact) {
return false;
} else {
}
else {
return true;
}
};
@ -609,17 +816,16 @@ define([
return !a ? "" : (a.type === "upper_bound" ? ">" : "<") + y_format_metric_value(a.threshold, metric);
};
$scope.detailViewLink = function (rows, fields) {
var
query,
time,
show,
from,
to;
query,
time,
show,
from,
to;
if (_.isUndefined(rows)) {
rows = _.where($scope.rows, {selected: true});
rows = $scope.selectedData();
}
rows = _.map(rows, function (row) {
query = $scope.panel.persistent_field + ':"' + row.id + '"';
@ -629,15 +835,15 @@ define([
};
});
if (rows.length === 0) {
/*jshint -W107 */
return "javascript:;";
return null;
}
rows = JSON.stringify(rows);
time = filterSrv.timeRange(false);
show;
if (!_.isUndefined(fields)) {
show = "&show=" + fields.join(",");
} else {
}
else {
show = "";
}
@ -663,23 +869,13 @@ define([
};
$scope.calculateWarnings = function () {
$scope.warnLevels = {_global_: {}};
$scope.warnLevels = {};
_.each($scope.panel.metrics, function (metric) {
$scope.warnLevels._global_[metric.field] = 0;
_.each(_.pluck($scope.rows, 'id'), function (id) {
var num, level, summary;
$scope.warnLevels[id] = $scope.warnLevels[id] || {};
summary = $scope.data[id + '_' + metric.field];
if (!summary) {
return; // no data
}
num = summary.mean;
level = $scope.alertLevel(metric, num);
$scope.warnLevels[id][metric.field] = level;
if (level > $scope.warnLevels._global_[metric.field]) {
$scope.warnLevels._global_[metric.field] = level;
$scope.warnLevels[metric.field] = 0;
_.each($scope.data, function (s) {
var level = (s[metric.field] || {}).alert_level;
if (!_.isUndefined(level) && level > $scope.warnLevels[metric.field]) {
$scope.warnLevels[metric.field] = level;
}
});
});
@ -700,7 +896,8 @@ define([
}
if (testAlert(metric.error, num)) {
level = 2;
} else if (testAlert(metric.warning, num)) {
}
else if (testAlert(metric.warning, num)) {
level = 1;
}
@ -708,7 +905,8 @@ define([
var r = Math.random();
if (r > 0.9) {
level = 2;
} else if (r > 0.8) {
}
else if (r > 0.8) {
level = 1;
}
@ -735,7 +933,8 @@ define([
if (s[0] === '<') {
ret.type = "lower_bound";
s = s.substr(1);
} else if (s[0] === '>') {
}
else if (s[0] === '>') {
s = s.substr(1);
}
@ -747,7 +946,7 @@ define([
};
$scope.addMetric = function (panel,metric) {
$scope.addMetric = function (panel, metric) {
metric = metric || {};
metric = metricDefaults(metric);
panel.metrics.push(metric);
@ -780,16 +979,21 @@ define([
index: -1
};
if ($scope._needs_refresh) {
$scope.get_rows();
$scope.get_data();
}
$scope._needs_refresh = false;
$scope.$emit('render');
};
$scope.deleteMetric = function (panel,index) {
$scope.deleteMetric = function (panel, index) {
panel.metrics = _.without(panel.metrics, panel.metrics[index]);
};
$scope.onRowFilterChange = _.debounce(function () {
$scope.$apply(function (scope) {
$scope.select_display_data_and_enrich(scope.data);
});
}, 500);
});
@ -807,7 +1011,8 @@ define([
// it is valid
ctrl.$setValidity('alertValue', true);
return scope.parseAlert(viewValue);
} else {
}
else {
// it is invalid, return undefined (no model update)
ctrl.$setValidity('alertValue', false);
return undefined;

View file

@ -4,7 +4,7 @@
"company": "Elasticsearch BV"
},
"name": "marvel",
"version": "1.0.3",
"version": "1.0.3-SNAPSHOT",
"repository": {
"type": "git",
"url": "http://github.com/elasticsearch/elasticsearch-marvel.git"

View file

@ -162,25 +162,50 @@ define([
input.focus();
};
/**
* Make the editor resizeable
*/
input.$el.resizable({
autoHide: false,
handles: 'e',
start: function (e, ui) {
$(".ui-resizable-e").addClass("active");
},
stop: function (e, ui) {
$(".ui-resizable-e").removeClass("active");
var parent = ui.element.parent();
var editorSize = ui.element.outerWidth();
output.$el.css("left", editorSize + 20);
input.$actions.css("margin-right", -editorSize + 3);
input.resize(true);
output.resize(true);
(function stuffThatsTooHardWithCSS() {
var $editors = input.$el.parent().add(output.$el.parent());
var $resizer = miscInputs.$resizer;
var $header = miscInputs.$header;
var delay;
var headerHeight;
var resizerHeight;
$resizer
.html('&#xFE19;') // vertical elipses
.css('vertical-align', 'middle');
function update() {
var newHeight;
delay = clearTimeout(delay);
newHeight = $header.outerHeight();
if (headerHeight != newHeight) {
headerHeight = newHeight;
$editors.css('top', newHeight + 10);
}
newHeight = $resizer.height();
if (resizerHeight != newHeight) {
resizerHeight = newHeight;
$resizer.css('line-height', newHeight + 'px');
}
}
});
// update at key moments in the loading process
$(update);
$(window).load(update);
// and when the window resizes (once every 30 ms)
$(window)
.resize(function (event) {
if (!delay && event.target === window) {
delay = setTimeout(update, 30);
}
});
}());
/**
* Setup the "send" shortcut
@ -191,14 +216,14 @@ define([
exec: function () {
output.update('');
submitCurrentRequestToES(function (resp) {
output.update(resp);
output.update(resp, 'ace/mode/json');
});
}
});
$send.click(function () {
submitCurrentRequestToES(function (resp) {
output.update(resp);
output.update(resp, 'ace/mode/json');
});
return false;
});
@ -206,7 +231,6 @@ define([
/*
* initialize navigation menu
*/
$.get('../common/marvelLinks.json', function (marvelLinks) {
var linkMenu = $("#nav_btn ul");
_.map(marvelLinks.links, function (link) {

View file

@ -25,7 +25,7 @@ define([
});
$helpPopup.on('hidden', function () {
$('#example_editor').remove();
$('#help_example_editor').remove();
});
return $helpPopup;

View file

@ -9,7 +9,8 @@ define([
'require',
'utils',
'zeroclip',
'ace_ext_language_tools'
'ace_ext_language_tools',
'ace_ext_searchbox'
], function (ace, Autocomplete, $, mappings, output, SenseEditor, settings, require, utils, ZeroClipboard) {
'use strict';
@ -29,6 +30,9 @@ define([
input.autocomplete = new Autocomplete(input);
input.$actions = $("#editor_actions");
ace.require('ace/ext/language_tools').addCompleter(input.autocomplete.completer);
input.commands.addCommand({
@ -74,7 +78,7 @@ define([
zc.on('wrongflash noflash', function () {
if (!localStorage.getItem('flash_warning_shown')) {
alert('Sense needs flash version 10.0 or greater in order to provide "Copy as cURL" functionality');
localStorage.setItem('flash_warning_shown');
localStorage.setItem('flash_warning_shown', 'true');
}
$copyAsCURL.hide();
});

View file

@ -5,11 +5,15 @@ define(function () {
filters.and = {
__template: {
filters: [{}]
filters: [
{}
]
},
filters: [{
__scope_link: '.filter'
}],
filters: [
{
__scope_link: '.filter'
}
],
_cache: {
__one_of: [false, true]
}
@ -17,15 +21,21 @@ define(function () {
filters.bool = {
must: [{
__scope_link: '.filter'
}],
must_not: [{
__scope_link: '.filter'
}],
should: [{
__scope_link: '.filter'
}],
must: [
{
__scope_link: '.filter'
}
],
must_not: [
{
__scope_link: '.filter'
}
],
should: [
{
__scope_link: '.filter'
}
],
_cache: {
__one_of: [false, true]
}
@ -166,20 +176,25 @@ define(function () {
filters.geo_polygon = {
__template: {
'FIELD': {
'points': [{
lat: 40.73,
lon: -74.1
}, {
lat: 40.83,
lon: -75.1
}]
'points': [
{
lat: 40.73,
lon: -74.1
},
{
lat: 40.83,
lon: -75.1
}
]
}
},
'$FIELD$': {
points: [{
lat: 40.73,
lon: -74.1
}]
points: [
{
lat: 40.73,
lon: -74.1
}
]
},
_cache: {
__one_of: [false, true]
@ -240,7 +255,7 @@ define(function () {
};
filters.m = filters.missing = {
filters.m = filters.missing = {
__template: {
field: 'FIELD'
},
@ -274,24 +289,30 @@ define(function () {
to: 20
}
},
from: 1,
to: 20,
include_lower: {
__one_of: [true, false]
},
include_upper: {
__one_of: [true, false]
"$FIELD$": {
from: 1,
to: 20,
include_lower: {
__one_of: [true, false]
},
include_upper: {
__one_of: [true, false]
}
}
};
filters.or = {
__template: {
filters: [{}]
filters: [
{}
]
},
filters: [{
__scope_link: '.filter'
}],
filters: [
{
__scope_link: '.filter'
}
],
_cache: {
__one_of: [false, true]
}
@ -335,16 +356,18 @@ define(function () {
to: 20
}
},
from: 1,
to: 20,
include_lower: {
__one_of: [true, false]
},
include_upper: {
__one_of: [true, false]
},
_cache: {
__one_of: [false, true]
"$FIELD$": {
from: 1,
to: 20,
include_lower: {
__one_of: [true, false]
},
include_upper: {
__one_of: [true, false]
},
_cache: {
__one_of: [false, true]
}
}
};

View file

@ -162,6 +162,10 @@ define([
per_index_types = {};
$.each(mappings, function (index, index_mapping) {
var normalized_index_mappings = {};
// 1.0.0 mapping format has changed, extract underlying mapping
if (index_mapping.mappings && _.keys(index_mapping).length === 1) {
index_mapping = index_mapping.mappings;
}
$.each(index_mapping, function (type_name, type_mapping) {
var field_list = getFieldNamesFromTypeMapping(type_mapping);
normalized_index_mappings[type_name] = field_list;

View file

@ -3,10 +3,11 @@ define([
'history',
'input',
'mappings',
'output',
'bootstrap',
'jquery-ui'
], function ($, history, input, mappings) {
], function ($, history, input, mappings, output) {
'use strict';
var $esServer = $("#es_server");
@ -32,9 +33,36 @@ define([
e.preventDefault();
});
var $header = $('.navbar.navbar-static-top');
// containers for the two editors
var $left = input.$el.parent();
var $right = output.$el.parent();
$left.resizable({
autoHide: false,
handles: 'e',
start: function () {
$resizer.addClass('active');
},
resize: function () {
$right.css('left', $left.outerWidth() + 20);
},
stop: function () {
$resizer.removeClass('active');
$left.css('height', 'auto'); // $.resizeable sets the height which prevents it from reshaping later
input.resize(true);
output.resize(true);
}
});
var $resizer = input.$el.siblings('.ui-resizable-e');
return {
$esServer: $esServer,
$send: $send,
$autoIndent: $autoIndent
$autoIndent: $autoIndent,
$header: $header,
$resizer: $resizer
};
})

View file

@ -7,16 +7,24 @@ define([
var $el = $("#output");
var output = ace.require('ace/ace').edit($el[0]);
output.update = function (val, cb) {
output.getSession().setValue(val);
output.update = function (val, mode, cb) {
if (typeof mode === 'function') {
cb = mode;
mode = void 0;
}
var session = output.getSession();
session.setMode(val ? (mode || 'ace/mode/json') : 'ace/mode/text');
session.setValue(val);
if (typeof cb === 'function') {
setTimeout(cb);
}
};
output.$el = $el;
output.getSession().setMode("ace/mode/json");
output.getSession().setMode("ace/mode/text");
output.getSession().setFoldStyle('markbeginend');
output.getSession().setUseWrapMode(true);
output.setShowPrintMargin(false);

View file

@ -16,6 +16,7 @@
'ace': '../vendor/ace/ace',
'ace_mode_json': '../vendor/ace/mode-json',
'ace_ext_language_tools': '../vendor/ace/ext-language_tools',
'ace_ext_searchbox': '../vendor/ace/ext-searchbox'
},
map: {
'*': {

View file

@ -55,7 +55,8 @@ define([
var CURRENT_REQ_RANGE = null;
editor.$el = $el;
editor.$actions = $("#editor_actions");
// place holder for an action bar, needs to be set externally.
editor.$actions = null;
// mixin the RowParser
editor.parser = new RowParser(editor);
@ -191,6 +192,10 @@ define([
var pos = currentReqRange.start;
var tokenIter = editor.iterForPosition(pos.row, pos.column, editor);
var t = tokenIter.getCurrentToken();
if (editor.parser.isEmptyToken(t)) {
// if the row starts with some spaces, skip them.
t = editor.parser.nextNonEmptyToken(tokenIter);
}
request.method = t.value;
t = editor.parser.nextNonEmptyToken(tokenIter);
if (!t || t.type == "method") return null;
@ -354,39 +359,54 @@ define([
editor.highlightCurrentRequestAndUpdateActionBar();
});
editor.updateActionsBar = function () {
var editor_actions = $("#editor_actions");
editor.updateActionsBar = (function () {
var set = function (top) {
if (top == null) {
editor.$actions.css('visibility', 'hidden');
} else {
editor.$actions.css({
top: top,
visibility: 'visible'
});
}
};
if (CURRENT_REQ_RANGE) {
var row = CURRENT_REQ_RANGE.start.row;
var column = CURRENT_REQ_RANGE.start.column;
var session = editor.session;
var firstLine = session.getLine(row);
var offset = 0;
if (firstLine.length > session.getScreenWidth() - 5) {
// overlap first row
if (row > 0) row--; else row++;
}
var screen_pos = editor.renderer.textToScreenCoordinates(row, column);
offset += screen_pos.pageY;
var end_offset = editor.renderer.textToScreenCoordinates(CURRENT_REQ_RANGE.end.row,
CURRENT_REQ_RANGE.end.column).pageY;
var hide = function () {
set();
};
offset = Math.min(end_offset, Math.max(offset, 47));
if (offset >= 47) {
editor_actions.css("top", Math.max(offset, 47));
editor_actions.css('visibility', 'visible');
return function () {
if (!editor.$actions) {
return;
}
else {
editor_actions.css("top", 0);
editor_actions.css('visibility', 'hidden');
if (CURRENT_REQ_RANGE) {
// elements are positioned relative to the editor's container
// pageY is relative to page, so subtract the offset
// from pageY to get the new top value
var offsetFromPage = editor.$el.offset().top;
var topOfReq = editor.renderer.textToScreenCoordinates(
CURRENT_REQ_RANGE.start.row,
CURRENT_REQ_RANGE.start.column
).pageY - offsetFromPage;
if (topOfReq >= 0) {
return set(topOfReq);
}
var bottomOfReq = editor.renderer.textToScreenCoordinates(
CURRENT_REQ_RANGE.end.row,
CURRENT_REQ_RANGE.end.column
).pageY - offsetFromPage;
if (bottomOfReq >= 0) {
return set(0);
}
}
hide();
}
else {
editor_actions.css("top", 0);
editor_actions.css('visibility', 'hidden');
}
};
}());
editor.getSession().on("changeScrollTop", editor.updateActionsBar);

View file

@ -1628,9 +1628,6 @@ define("sense_editor/mode/worker_parser", ['require', 'exports', 'module' ], fun
newLine();
strictWhite();
if (ch == '{') {
if (meth == "GET") {
annotate("warning", "Browsers do not support " + meth + " requests with a body. This will be executed as a POST.");
}
object();
}
// multi doc request
@ -1658,8 +1655,15 @@ define("sense_editor/mode/worker_parser", ['require', 'exports', 'module' ], fun
multi_request = function () {
while (ch && ch != '') {
white();
if (!ch) {
continue;
}
try {
comment();
white();
if (!ch) {
continue;
}
request();
white();
}

View file

@ -61,6 +61,7 @@ define([
url: url,
data: method == "GET" ? null : data,
password: password,
cache: false,
username: uname,
crossDomain: true,
type: method,

View file

@ -1,26 +0,0 @@
<h4>Quick intro to the UI</h4>
<p>Sense is split into two panes: an editor pane (white) and a response pane (black).
Use the editor to type requests and submit them to Elasticsearch. The results will be displayed in
the response pane on the right side.
</p>
<p>Sense understands requests in a compact format, similar to cURL:
<div id="welcome_example_container"></div>
<p>While typing a request, Sense will make suggestions which you can than accept by hitting Enter/Tab.
These suggestions are made based on the request structure <i>as well as</i> your indices and types.
</p>
<h4>A few quick tips, while I have your attention</h4>
<ul>
<li>Submit requests to ES using the green triangle button.</li>
<li>Use the wrench menu for other useful things.</li>
<li>You can paste requests in cURL format and they will be translated to the Sense syntax.</li>
<li>You can resize the editor and output panes by dragging the separator between them.</li>
<li>Study the keyboard shortcuts under the Help button. Good stuff in there!</li>
</ul>

View file

@ -20,13 +20,30 @@ body.fouc {
width: 100px;
}
#editor_container {
position: absolute;
top: 50px;
bottom: 5px;
left: 5px;
width: 468px;
}
#output_container {
position: absolute;
top: 50px;
bottom: 5px;
left: 488px;
right: 5px;
}
#editor_actions {
position: absolute;
top: 47px;
right: 100%;
top: 0;
right: 0;
line-height: 1;
margin-right: -468px;
z-index: 100;
padding: 1px 3px 0 0;
/* by pass any other element in ace and resize bar, but not modal popups */
z-index: 1005;
}
#editor_actions > .btn-group > a {
@ -44,25 +61,9 @@ body.fouc {
z-index: 100;
}
#editor_container {
position: absolute;
top: 50px;
bottom: 5px;
left: 5px;
width: 468px;
}
#output_container {
position: absolute;
top: 50px;
bottom: 5px;
left: 488px;
right: 5px;
min-width: 700px;
}
#output, #editor {
height: 100%;
width: 100%;
}
.ace_gutter {
@ -72,7 +73,8 @@ body.fouc {
#autocomplete {
visibility: hidden;
position: absolute;
z-index: 1000;
/* by pass any other element in ace and resize bar, but not modal popups */
z-index: 1003;
margin-top: 22px;
/*font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;*/
/*font-size: 12px;*/
@ -217,17 +219,16 @@ body.fouc {
.ui-resizable-e {
cursor: ew-resize;
width: 10px;
right: -5px;
top: 0;
bottom: 0;
width: 13px;
right: -14px;
top: -1px;
bottom: -2px;
background-color: transparent;
position: absolute;
z-index: 50 !important;
}
.ui-resizable-e:hover {
background-color: rgba(194, 193, 208, 0.80);
background-color: #DCDCDC;
}
.ui-resizable-e.active {

View file

@ -7,7 +7,7 @@
/* sadly ace doesn't carry the theme over to the auto complete popup */
.ace_autocomplete {
background-color: #2e3236 !important;
color: #bbbfc2 !important;
color: rgb(233, 233, 233) !important;
}
@ -15,9 +15,14 @@
color: #AE81FF !important;
}
.ace_selected {
background: #222 !important;
.ace_selection {
background: #575858 !important;
}
.ace_selected {
background: #575858 !important;
}
/* end of theming for auto complete- remove when issue in ace is fixed. */
.ace_url {

View file

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<title>Sense - a JSON aware interface to ElasticSearch</title>
<title>Marvel - Sense</title>
<link id="bootstrapThemeCss" href="vendor/bootstrap/css/bootstrap.light.min.css" type="text/css" rel="stylesheet"/>
<link href="vendor/font-awesome/css/font-awesome.min.css" type="text/css" rel="stylesheet"/>
<link id="senseThemeCss" href="css/sense.light.css" rel="stylesheet" type="text/css"/>
@ -46,22 +46,21 @@
<div id="editor">GET _search
{
"query": { "match_all": {} }
}
}</div>
<div id="editor_actions">
<a id="send" href="#" data-toggle="tooltip" title="click to send request"><i
class="fa fa-play"></i></a>
<a id="request_wrench" data-toggle="dropdown" href="#"><i
class="fa fa-wrench"></i></a>
<ul class="dropdown-menu">
<li><a id="copy_as_curl" tabindex="-1" href="#">Copy as cURL</a></li>
<li><a id="auto_indent" tabindex="-1" href="#">Auto indent</a></li>
</ul>
</div>
</div>
<div id="output_container">
<div id="output">{}</div>
</div>
<div id="editor_actions">
<a id="send" href="#" data-toggle="tooltip" title="click to send request"><i
class="fa fa-play"></i></a>
<a id="request_wrench" data-toggle="dropdown" href="#"><i
class="fa fa-wrench"></i></a>
<ul class="dropdown-menu">
<li><a id="copy_as_curl" tabindex="-1" href="#">Copy as cURL</a></li>
<li><a id="auto_indent" tabindex="-1" href="#">Auto indent</a></li>
</ul>
</div>
<ul id="autocomplete" style="left: -1000px;"></ul>
</div>
@ -177,12 +176,35 @@
</div>
<div id="welcome_popup" class="modal hide fade" data-keyboard="true" data-remote="app/welcome.html">
<div id="welcome_popup" class="modal hide fade" data-keyboard="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Welcome to Sense</h3>
</div>
<div class="modal-body">
<h4>Quick intro to the UI</h4>
<p>Sense is split into two panes: an editor pane (white) and a response pane (black).
Use the editor to type requests and submit them to Elasticsearch. The results will be displayed in
the response pane on the right side.
</p>
<p>Sense understands requests in a compact format, similar to cURL:
<div id="welcome_example_container"></div>
<p>While typing a request, Sense will make suggestions which you can than accept by hitting Enter/Tab.
These suggestions are made based on the request structure <i>as well as</i> your indices and types.
</p>
<h4>A few quick tips, while I have your attention</h4>
<ul>
<li>Submit requests to ES using the green triangle button.</li>
<li>Use the wrench menu for other useful things.</li>
<li>You can paste requests in cURL format and they will be translated to the Sense syntax.</li>
<li>You can resize the editor and output panes by dragging the separator between them.</li>
<li>Study the keyboard shortcuts under the Help button. Good stuff in there!</li>
</ul>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" data-dismiss="modal" aria-hidden="true">Get to work</a>

View file

@ -88,6 +88,31 @@ define([
});
});
utils_test("simple request range, prefixed with spaces", " " + simple_request.prefix, simple_request.data, function () {
input.getCurrentRequestRange(function (range) {
var expected = new aceRange.Range(
0, 0,
3, 1
);
deepEqual(range, expected);
start();
});
});
utils_test("simple request data, prefixed with spaces", " " + simple_request.prefix, simple_request.data, function () {
input.getCurrentRequest(function (request) {
var expected = {
method: "POST",
url: "_search",
data: [simple_request.data]
};
deepEqual(request, expected);
start();
});
});
utils_test("single line request range", single_line_request.prefix, single_line_request.data, function () {
input.getCurrentRequestRange(function (range) {
var expected = new aceRange.Range(

View file

@ -71,6 +71,26 @@ define([
deepEqual(mappings.getFields("index").sort(fc), [f("number", "int"), f("str", "string") ]);
});
test("Simple fields - 1.0 style", function () {
mappings.loadMappings({
"index": {
"mappings": {
"tweet": {
"properties": {
"str": {
"type": "string"
},
"number": {
"type": "int"
}
}
}
}
}
});
deepEqual(mappings.getFields("index").sort(fc), [f("number", "int"), f("str", "string") ]);
});
test("Nested fields", function () {
mappings.loadMappings({
@ -208,13 +228,13 @@ define([
});
deepEqual(mappings.getIndices().sort(),
[ "_all", "alias1", "alias2", "test_index1", "test_index2" ]
[ "_all", "alias1", "alias2", "test_index1", "test_index2" ]
);
deepEqual(mappings.getIndices(false).sort(),
["test_index1", "test_index2" ]
["test_index1", "test_index2" ]
);
deepEqual(mappings.expandAliases(["alias1", "test_index2"]).sort(),
["test_index1", "test_index2" ]
["test_index1", "test_index2" ]
);
deepEqual(mappings.expandAliases("alias2"), "test_index2");
});

421
sense/vendor/ace/ext-searchbox.js vendored Normal file
View file

@ -0,0 +1,421 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
ace.define('ace/ext/searchbox', ['require', 'exports', 'module' , 'ace/lib/dom', 'ace/lib/lang', 'ace/lib/event', 'ace/keyboard/hash_handler', 'ace/lib/keys'], function(require, exports, module) {
var dom = require("../lib/dom");
var lang = require("../lib/lang");
var event = require("../lib/event");
var searchboxCss = "\
/* ------------------------------------------------------------------------------------------\
* Editor Search Form\
* --------------------------------------------------------------------------------------- */\
.ace_search {\
background-color: #ddd;\
border: 1px solid #cbcbcb;\
border-top: 0 none;\
max-width: 297px;\
overflow: hidden;\
margin: 0;\
padding: 4px;\
padding-right: 6px;\
padding-bottom: 0;\
position: absolute;\
top: 0px;\
z-index: 99;\
white-space: normal;\
}\
.ace_search.left {\
border-left: 0 none;\
border-radius: 0px 0px 5px 0px;\
left: 0;\
}\
.ace_search.right {\
border-radius: 0px 0px 0px 5px;\
border-right: 0 none;\
right: 0;\
}\
.ace_search_form, .ace_replace_form {\
border-radius: 3px;\
border: 1px solid #cbcbcb;\
float: left;\
margin-bottom: 4px;\
overflow: hidden;\
}\
.ace_search_form.ace_nomatch {\
outline: 1px solid red;\
}\
.ace_search_field {\
background-color: white;\
border-right: 1px solid #cbcbcb;\
border: 0 none;\
-webkit-box-sizing: border-box;\
-moz-box-sizing: border-box;\
box-sizing: border-box;\
display: block;\
float: left;\
height: 22px;\
outline: 0;\
padding: 0 7px;\
width: 214px;\
margin: 0;\
}\
.ace_searchbtn,\
.ace_replacebtn {\
background: #fff;\
border: 0 none;\
border-left: 1px solid #dcdcdc;\
cursor: pointer;\
display: block;\
float: left;\
height: 22px;\
margin: 0;\
padding: 0;\
position: relative;\
}\
.ace_searchbtn:last-child,\
.ace_replacebtn:last-child {\
border-top-right-radius: 3px;\
border-bottom-right-radius: 3px;\
}\
.ace_searchbtn:disabled {\
background: none;\
cursor: default;\
}\
.ace_searchbtn {\
background-position: 50% 50%;\
background-repeat: no-repeat;\
width: 27px;\
}\
.ace_searchbtn.prev {\
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=); \
}\
.ace_searchbtn.next {\
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=); \
}\
.ace_searchbtn_close {\
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;\
border-radius: 50%;\
border: 0 none;\
color: #656565;\
cursor: pointer;\
display: block;\
float: right;\
font-family: Arial;\
font-size: 16px;\
height: 14px;\
line-height: 16px;\
margin: 5px 1px 9px 5px;\
padding: 0;\
text-align: center;\
width: 14px;\
}\
.ace_searchbtn_close:hover {\
background-color: #656565;\
background-position: 50% 100%;\
color: white;\
}\
.ace_replacebtn.prev {\
width: 54px\
}\
.ace_replacebtn.next {\
width: 27px\
}\
.ace_button {\
margin-left: 2px;\
cursor: pointer;\
-webkit-user-select: none;\
-moz-user-select: none;\
-o-user-select: none;\
-ms-user-select: none;\
user-select: none;\
overflow: hidden;\
opacity: 0.7;\
border: 1px solid rgba(100,100,100,0.23);\
padding: 1px;\
-moz-box-sizing: border-box;\
box-sizing: border-box;\
color: black;\
}\
.ace_button:hover {\
background-color: #eee;\
opacity:1;\
}\
.ace_button:active {\
background-color: #ddd;\
}\
.ace_button.checked {\
border-color: #3399ff;\
opacity:1;\
}\
.ace_search_options{\
margin-bottom: 3px;\
text-align: right;\
-webkit-user-select: none;\
-moz-user-select: none;\
-o-user-select: none;\
-ms-user-select: none;\
user-select: none;\
}";
var HashHandler = require("../keyboard/hash_handler").HashHandler;
var keyUtil = require("../lib/keys");
dom.importCssString(searchboxCss, "ace_searchbox");
var html = '<div class="ace_search right">\
<button type="button" action="hide" class="ace_searchbtn_close"></button>\
<div class="ace_search_form">\
<input class="ace_search_field" placeholder="Search for" spellcheck="false"></input>\
<button type="button" action="findNext" class="ace_searchbtn next"></button>\
<button type="button" action="findPrev" class="ace_searchbtn prev"></button>\
</div>\
<div class="ace_replace_form">\
<input class="ace_search_field" placeholder="Replace with" spellcheck="false"></input>\
<button type="button" action="replaceAndFindNext" class="ace_replacebtn">Replace</button>\
<button type="button" action="replaceAll" class="ace_replacebtn">All</button>\
</div>\
<div class="ace_search_options">\
<span action="toggleRegexpMode" class="ace_button" title="RegExp Search">.*</span>\
<span action="toggleCaseSensitive" class="ace_button" title="CaseSensitive Search">Aa</span>\
<span action="toggleWholeWords" class="ace_button" title="Whole Word Search">\\b</span>\
</div>\
</div>'.replace(/>\s+/g, ">");
var SearchBox = function(editor, range, showReplaceForm) {
var div = dom.createElement("div");
div.innerHTML = html;
this.element = div.firstChild;
this.$init();
this.setEditor(editor);
};
(function() {
this.setEditor = function(editor) {
editor.searchBox = this;
editor.container.appendChild(this.element);
this.editor = editor;
};
this.$initElements = function(sb) {
this.searchBox = sb.querySelector(".ace_search_form");
this.replaceBox = sb.querySelector(".ace_replace_form");
this.searchOptions = sb.querySelector(".ace_search_options");
this.regExpOption = sb.querySelector("[action=toggleRegexpMode]");
this.caseSensitiveOption = sb.querySelector("[action=toggleCaseSensitive]");
this.wholeWordOption = sb.querySelector("[action=toggleWholeWords]");
this.searchInput = this.searchBox.querySelector(".ace_search_field");
this.replaceInput = this.replaceBox.querySelector(".ace_search_field");
};
this.$init = function() {
var sb = this.element;
this.$initElements(sb);
var _this = this;
event.addListener(sb, "mousedown", function(e) {
setTimeout(function(){
_this.activeInput.focus();
}, 0);
event.stopPropagation(e);
});
event.addListener(sb, "click", function(e) {
var t = e.target || e.srcElement;
var action = t.getAttribute("action");
if (action && _this[action])
_this[action]();
else if (_this.$searchBarKb.commands[action])
_this.$searchBarKb.commands[action].exec(_this);
event.stopPropagation(e);
});
event.addCommandKeyListener(sb, function(e, hashId, keyCode) {
var keyString = keyUtil.keyCodeToString(keyCode);
var command = _this.$searchBarKb.findKeyCommand(hashId, keyString);
if (command && command.exec) {
command.exec(_this);
event.stopEvent(e);
}
});
this.$onChange = lang.delayedCall(function() {
_this.find(false, false);
});
event.addListener(this.searchInput, "input", function() {
_this.$onChange.schedule(20);
});
event.addListener(this.searchInput, "focus", function() {
_this.activeInput = _this.searchInput;
_this.searchInput.value && _this.highlight();
});
event.addListener(this.replaceInput, "focus", function() {
_this.activeInput = _this.replaceInput;
_this.searchInput.value && _this.highlight();
});
};
this.$closeSearchBarKb = new HashHandler([{
bindKey: "Esc",
name: "closeSearchBar",
exec: function(editor) {
editor.searchBox.hide();
}
}]);
this.$searchBarKb = new HashHandler();
this.$searchBarKb.bindKeys({
"Ctrl-f|Command-f|Ctrl-H|Command-Option-F": function(sb) {
var isReplace = sb.isReplace = !sb.isReplace;
sb.replaceBox.style.display = isReplace ? "" : "none";
sb[isReplace ? "replaceInput" : "searchInput"].focus();
},
"Ctrl-G|Command-G": function(sb) {
sb.findNext();
},
"Ctrl-Shift-G|Command-Shift-G": function(sb) {
sb.findPrev();
},
"esc": function(sb) {
setTimeout(function() { sb.hide();});
},
"Return": function(sb) {
if (sb.activeInput == sb.replaceInput)
sb.replace();
sb.findNext();
},
"Shift-Return": function(sb) {
if (sb.activeInput == sb.replaceInput)
sb.replace();
sb.findPrev();
},
"Tab": function(sb) {
(sb.activeInput == sb.replaceInput ? sb.searchInput : sb.replaceInput).focus();
}
});
this.$searchBarKb.addCommands([{
name: "toggleRegexpMode",
bindKey: {win: "Alt-R|Alt-/", mac: "Ctrl-Alt-R|Ctrl-Alt-/"},
exec: function(sb) {
sb.regExpOption.checked = !sb.regExpOption.checked;
sb.$syncOptions();
}
}, {
name: "toggleCaseSensitive",
bindKey: {win: "Alt-C|Alt-I", mac: "Ctrl-Alt-R|Ctrl-Alt-I"},
exec: function(sb) {
sb.caseSensitiveOption.checked = !sb.caseSensitiveOption.checked;
sb.$syncOptions();
}
}, {
name: "toggleWholeWords",
bindKey: {win: "Alt-B|Alt-W", mac: "Ctrl-Alt-B|Ctrl-Alt-W"},
exec: function(sb) {
sb.wholeWordOption.checked = !sb.wholeWordOption.checked;
sb.$syncOptions();
}
}]);
this.$syncOptions = function() {
dom.setCssClass(this.regExpOption, "checked", this.regExpOption.checked);
dom.setCssClass(this.wholeWordOption, "checked", this.wholeWordOption.checked);
dom.setCssClass(this.caseSensitiveOption, "checked", this.caseSensitiveOption.checked);
this.find(false, false);
};
this.highlight = function(re) {
this.editor.session.highlight(re || this.editor.$search.$options.re);
this.editor.renderer.updateBackMarkers()
};
this.find = function(skipCurrent, backwards) {
var range = this.editor.find(this.searchInput.value, {
skipCurrent: skipCurrent,
backwards: backwards,
wrap: true,
regExp: this.regExpOption.checked,
caseSensitive: this.caseSensitiveOption.checked,
wholeWord: this.wholeWordOption.checked
});
var noMatch = !range && this.searchInput.value;
dom.setCssClass(this.searchBox, "ace_nomatch", noMatch);
this.editor._emit("findSearchBox", { match: !noMatch });
this.highlight();
};
this.findNext = function() {
this.find(true, false);
};
this.findPrev = function() {
this.find(true, true);
};
this.replace = function() {
if (!this.editor.getReadOnly())
this.editor.replace(this.replaceInput.value);
};
this.replaceAndFindNext = function() {
if (!this.editor.getReadOnly()) {
this.editor.replace(this.replaceInput.value);
this.findNext()
}
};
this.replaceAll = function() {
if (!this.editor.getReadOnly())
this.editor.replaceAll(this.replaceInput.value);
};
this.hide = function() {
this.element.style.display = "none";
this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb);
this.editor.focus();
};
this.show = function(value, isReplace) {
this.element.style.display = "";
this.replaceBox.style.display = isReplace ? "" : "none";
this.isReplace = isReplace;
if (value)
this.searchInput.value = value;
this.searchInput.focus();
this.searchInput.select();
this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb);
};
}).call(SearchBox.prototype);
exports.SearchBox = SearchBox;
exports.Search = function(editor, isReplace) {
var sb = editor.searchBox || new SearchBox(editor);
sb.show(editor.session.getTextRange(), isReplace);
};
});

View file

@ -16,7 +16,8 @@ module.exports = function (grunt) {
'build_sense',
'copy:artifacts_to_build',
'clean:build_tmp',
'replace:git_commits'
'replace:git_commits',
'replace:kibana_replace_title'
]);
grunt.registerTask('build_sense', [

View file

@ -53,6 +53,21 @@ module.exports = function (config) {
cwd: '<%= buildSiteDir %>',
expand: true, src: ['**/*'], dest: '<%= buildSiteDir %>/'}
]
},
kibana_replace_title: {
options: {
patterns: [
{
match: /<title>.*?<\/title>/,
replacement: '<title>{{dashboard.current.title ? dashboard.current.title : "Marvel"}}</title>'
}
]
},
files: [
{
cwd: '<%= buildSiteDir %>',
expand: true, src: ['kibana/index.html'], dest: '<%= buildSiteDir %>/'}
]
}
}
};