diff --git a/LICENSE b/LICENSE index d159169..261eeb9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,339 +1,201 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 72e2b5d..aabb0e3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ +

@@ -18,7 +19,12 @@

-_wakatime_exporter_ is a Prometheus exporter for Wakatime statistics. It intends to extend the existing Wakatime ecosystem via allowing users to make use of Prometheus (and therefore any consumers of its API) as companion services alongside the traditional Wakatime web application. This could be anything from including some of your coding statistics in Grafana, to forecasting your coding time with prophet. Sky's the limit! +_wakatime_exporter_ is a Prometheus exporter for [Wakatime](https://wakatime.com/) statistics. +It intends to extend the existing Wakatime ecosystem +via allowing users to make use of Prometheus (and therefore any consumers of its API) +as companion services alongside the traditional Wakatime web application. +This could be anything from including some of your coding statistics in Grafana, +to forecasting your coding time with prophet. > NOTE: _wakatime_exporter_ is currently in ALPHA. Expect things to break and change. @@ -31,7 +37,8 @@ _wakatime_exporter_ is a Prometheus exporter for Wakatime statistics. It intends ## Usage In most cases, you should only need to provide an API key. -All other parameters are for advanced use-cases only and you should be able to leave them set to their defaults. +All other parameters are for advanced use-cases only +and you should be able to leave them set to their defaults. You can get your Wakatime API key by visiting: https://wakatime.com/api-key @@ -41,32 +48,43 @@ Provide arguments via parameters: usage: wakatime_exporter --wakatime.api-key=WAKATIME.API-KEY [] Flags: - --help Show context-sensitive help. - --web.listen-address=":9212" Address to listen on for web interface and telemetry. - --web.telemetry-path="/metrics" Path under which to expose metrics. + --help, -h Show context-sensitive help. + --collector.all-time Enable the all-time collector (default: enabled). + --collector.goal Enable the goal collector (default: enabled). + --collector.leader Enable the leader collector (default: enabled). + --collector.summary Enable the summary collector (default: enabled). + --collector.disable-defaults Set all collectors to disabled by default. + --web.listen-address=":9212" Address to listen on for web interface and telemetry. + --web.metrics-path="/metrics" Path under which to expose metrics. + --web.disable-exporter-metrics Exclude metrics about the exporter itself (promhttp_*, process_*, go_*). --wakatime.scrape-uri="https://wakatime.com/api/v1" - Base path to query for Wakatime data. - --wakatime.user="current" User to query for Wakatime data. - --wakatime.api-key=WAKATIME.API-KEY Token to use when getting stats from Wakatime. - --wakatime.timeout=5s Timeout for trying to get stats from Wakatime. - --wakatime.ssl-verify Flag that enables SSL certificate verification. - --log.level=info Only log messages with the given severity or above. - One of: [debug, info, warn, error] - --log.format=logfmt Output format of log messages. - One of: [logfmt, json] - --version Show application version. + Base path to query for Wakatime data. + --wakatime.user="current" User to query for Wakatime data. + --wakatime.api-key Token to use when getting stats from Wakatime. + --wakatime.timeout=5s Timeout for trying to get stats from Wakatime. + --wakatime.ssl-verify Flag that enables SSL certificate verification for the scrape URI. + --log.level=info Only log messages with the given severity or above. + One of: [debug, info, warn, error] + --log.format=logfmt Output format of log messages. + One of: [logfmt, json] + --version Show application version. ``` and/or via environment variables: -``` -WAKA_LISTEN_ADDR=":9212" # Address to listen on for web interface and telemetry. +```shell +WAKA_LISTEN_ADDRESS=":9212" # Address to listen on for web interface and telemetry. WAKA_METRICS_PATH="/metrics" # Path under which to expose metrics. WAKA_SCRAPE_URI="https://wakatime.com/api/v1" # Base path to query for Wakatime data. WAKA_USER="current" # User to query for Wakatime data. WAKA_API_KEY="" # Token to use when getting stats from Wakatime. WAKA_TIMEOUT="5s" # Timeout for trying to get stats from Wakatime. WAKA_SSL_VERIFY="true" # SSL certificate verification for the scrape URI. +WAKA_DISABLE_EXPORTER_METRICS="false" # Exclude metrics about the exporter itself. +WAKA_COLLECTOR_ALLTIME="true" # Enable the all-time collector. +WAKA_COLLECTOR_GOAL="true" # Enable the goal collector. +WAKA_COLLECTOR_LEADER="true" # Enable the leader collector. +WAKA_COLLECTOR_SUMMARY="true" # Enable the summary collector. ``` ## Docker @@ -74,3 +92,23 @@ WAKA_SSL_VERIFY="true" # SSL certificate verification for ```shell docker run -p 9212:9212 macropower/wakatime-exporter:latest --wakatime.api-key="YOUR_API_KEY" ``` + +## Compatibility + +_wakatime_exporter_ is designed to work with [Wakatime](https://wakatime.com/). +However, you can additionally use any other application +that is compliant with the [Wakatime API](https://wakatime.com/developers), +e.g. [wakapi](https://github.com/muety/wakapi), +via `--wakatime.scrape-uri` or `WAKA_SCRAPE_URI`. +If said application only implements portions of the Wakatime API, +you can disable collectors for any non-compliant or non-existent endpoints +using parameters or environment variables as described in [usage](#usage). + +## License + +This project was licensed GPL-2.0 from 0.0.0 to 0.0.5. + +As of 0.0.6, parts of this application use code from node_exporter, which is licensed APACHE 2.0. + +As of 0.0.6, this project in its entirety, excluding any packages found in vendor, is licensed APACHE 2.0. +Each file in this project has a license header which identifies the copyright for said file. diff --git a/collector/all-time.go b/collector/all-time.go new file mode 100644 index 0000000..18371b7 --- /dev/null +++ b/collector/all-time.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package collector + +import ( + "errors" + "io" + "net/url" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + allTimeCollector = "all-time" + allTimeSubsystem = "cumulative" + allTimeEndpoint = "all_time_since_today" +) + +type alltimeCollector struct { + total *prometheus.Desc + uri url.URL + fetchStat func(url.URL, string, url.Values) (io.ReadCloser, error) + logger log.Logger +} + +func init() { + registerCollector(allTimeCollector, defaultEnabled, NewAllTimeCollector) +} + +// NewAllTimeCollector returns a new Collector exposing all-time stats. +func NewAllTimeCollector(in CommonInputs, logger log.Logger) (Collector, error) { + return &alltimeCollector{ + total: prometheus.NewDesc( + prometheus.BuildFQName(namespace, allTimeSubsystem, "seconds_total"), + "Total seconds (all time).", + nil, nil, + ), + uri: in.URI, + fetchStat: FetchHTTP(in.Token, in.SSLVerify, in.Timeout, logger), + logger: logger, + }, nil +} + +func (c *alltimeCollector) Update(ch chan<- prometheus.Metric) error { + params := url.Values{} + params.Add("cache", "false") + + body, fetchErr := c.fetchStat(c.uri, allTimeEndpoint, params) + if fetchErr != nil { + return fetchErr + } + + alltimeStats := wakatimeAlltime{} + if err := ReadAndUnmarshal(body, &alltimeStats); err != nil { + return err + } + + defer body.Close() + + level.Info(c.logger).Log( + "msg", "Collecting all-time from Wakatime", + "IsUpToDate", alltimeStats.Data.IsUpToDate, + ) + if alltimeStats.Data.IsUpToDate == true { + ch <- prometheus.MustNewConstMetric( + c.total, + prometheus.CounterValue, + alltimeStats.Data.TotalSeconds, + ) + } else { + return errors.New("skipped scrape of all-time metrics because they were not up to date") + } + + return nil +} diff --git a/collector/all-time_types.go b/collector/all-time_types.go new file mode 100644 index 0000000..cc30690 --- /dev/null +++ b/collector/all-time_types.go @@ -0,0 +1,24 @@ +/* +Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package collector + +type wakatimeAlltime struct { + Data struct { + IsUpToDate bool `json:"is_up_to_date"` + Text string `json:"text"` + TotalSeconds float64 `json:"total_seconds"` + } `json:"data"` +} diff --git a/collector/collector.go b/collector/collector.go new file mode 100644 index 0000000..03c20cd --- /dev/null +++ b/collector/collector.go @@ -0,0 +1,206 @@ +/* +Copyright 2015 The Prometheus Authors +Modifications Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package collector + +import ( + "errors" + "fmt" + "net/url" + "regexp" + "strings" + "sync" + "time" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" + "github.com/prometheus/client_golang/prometheus" + kingpin "gopkg.in/alecthomas/kingpin.v2" +) + +var ( + scrapeDurationDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "scrape", "collector_duration_seconds"), + "wakatime_exporter: Duration of a collector scrape.", + []string{"collector"}, + nil, + ) + scrapeSuccessDesc = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "scrape", "collector_success"), + "wakatime_exporter: Whether a collector succeeded.", + []string{"collector"}, + nil, + ) +) + +const ( + defaultEnabled = true + defaultDisabled = false +) + +var ( + factories = make(map[string]func(in CommonInputs, logger log.Logger) (Collector, error)) + collectorState = make(map[string]*bool) + forcedCollectors = map[string]bool{} // collectors which have been explicitly enabled or disabled +) + +// CommonInputs are the inputs needed to implement any Collector +type CommonInputs struct { + BaseURI url.URL + URI url.URL + Token string + SSLVerify bool + Timeout time.Duration +} + +func registerCollector(collector string, isDefaultEnabled bool, factory func(in CommonInputs, logger log.Logger) (Collector, error)) { + var helpDefaultState string + if isDefaultEnabled { + helpDefaultState = "enabled" + } else { + helpDefaultState = "disabled" + } + + flagName := fmt.Sprintf("collector.%s", collector) + flagHelp := fmt.Sprintf("Enable the %s collector (default: %s).", collector, helpDefaultState) + defaultValue := fmt.Sprintf("%v", isDefaultEnabled) + + reg, _ := regexp.Compile("[^a-zA-Z0-9]+") + envar := "WAKA_COLLECTOR_" + strings.ToUpper(reg.ReplaceAllString(collector, "")) + + flag := kingpin.Flag(flagName, flagHelp).Default(defaultValue).Envar(envar).Action(collectorFlagAction(collector)).Bool() + collectorState[collector] = flag + + factories[collector] = factory +} + +// WakaCollector implements the prometheus.Collector interface. +type WakaCollector struct { + Collectors map[string]Collector + logger log.Logger +} + +// DisableDefaultCollectors sets the collector state to false for all collectors which +// have not been explicitly enabled on the command line. +func DisableDefaultCollectors() { + for c := range collectorState { + if _, ok := forcedCollectors[c]; !ok { + *collectorState[c] = false + } + } +} + +// collectorFlagAction generates a new action function for the given collector +// to track whether it has been explicitly enabled or disabled from the command line. +// A new action function is needed for each collector flag because the ParseContext +// does not contain information about which flag called the action. +// See: https://github.com/alecthomas/kingpin/issues/294 +func collectorFlagAction(collector string) func(ctx *kingpin.ParseContext) error { + return func(ctx *kingpin.ParseContext) error { + forcedCollectors[collector] = true + return nil + } +} + +// NewWakaCollector creates a new Collector. +func NewWakaCollector(in CommonInputs, logger log.Logger, filters ...string) (*WakaCollector, error) { + f := make(map[string]bool) + for _, filter := range filters { + enabled, exist := collectorState[filter] + if !exist { + return nil, fmt.Errorf("missing collector: %s", filter) + } + if !*enabled { + return nil, fmt.Errorf("disabled collector: %s", filter) + } + f[filter] = true + } + collectors := make(map[string]Collector) + for key, enabled := range collectorState { + if *enabled { + collector, err := factories[key](in, log.With(logger, "collector", key)) + if err != nil { + return nil, err + } + if len(f) == 0 || f[key] { + collectors[key] = collector + } + } + } + return &WakaCollector{Collectors: collectors, logger: logger}, nil +} + +// Describe implements the prometheus.Collector interface. +func (n WakaCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- scrapeDurationDesc + ch <- scrapeSuccessDesc +} + +// Collect implements the prometheus.Collector interface. +func (n WakaCollector) Collect(ch chan<- prometheus.Metric) { + wg := sync.WaitGroup{} + wg.Add(len(n.Collectors)) + for name, c := range n.Collectors { + go func(name string, c Collector) { + execute(name, c, ch, n.logger) + wg.Done() + }(name, c) + } + wg.Wait() +} + +func execute(name string, c Collector, ch chan<- prometheus.Metric, logger log.Logger) { + begin := time.Now() + err := c.Update(ch) + duration := time.Since(begin) + var success float64 + + if err != nil { + if isNoDataError(err) { + level.Debug(logger).Log("msg", "collector returned no data", "name", name, "duration_seconds", duration.Seconds(), "err", err) + } else { + level.Error(logger).Log("msg", "collector failed", "name", name, "duration_seconds", duration.Seconds(), "err", err) + } + success = 0 + } else { + level.Debug(logger).Log("msg", "collector succeeded", "name", name, "duration_seconds", duration.Seconds()) + success = 1 + } + ch <- prometheus.MustNewConstMetric(scrapeDurationDesc, prometheus.GaugeValue, duration.Seconds(), name) + ch <- prometheus.MustNewConstMetric(scrapeSuccessDesc, prometheus.GaugeValue, success, name) +} + +// Collector is the interface a collector has to implement. +type Collector interface { + // Get new metrics and expose them via prometheus registry. + Update(ch chan<- prometheus.Metric) error +} + +type typedDesc struct { + desc *prometheus.Desc + valueType prometheus.ValueType +} + +func (d *typedDesc) mustNewConstMetric(value float64, labels ...string) prometheus.Metric { + return prometheus.MustNewConstMetric(d.desc, d.valueType, value, labels...) +} + +// ErrNoData indicates the collector found no data to collect, but had no other error. +var ErrNoData = errors.New("collector returned no data") + +func isNoDataError(err error) bool { + return err == ErrNoData +} diff --git a/collector/common.go b/collector/common.go new file mode 100644 index 0000000..a59ea2a --- /dev/null +++ b/collector/common.go @@ -0,0 +1,97 @@ +/* +Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package collector + +import ( + "crypto/tls" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "path" + "time" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" +) + +// Namespace defines the common namespace to be used by all metrics. +const namespace = "wakatime" + +// BoolToBinary converts booleans to "0" or "1" +func BoolToBinary(b bool) string { + if b { + return "1" + } + return "0" +} + +// FetchHTTP is a generic fetch method for Wakatime API endpoints +func FetchHTTP(token string, sslVerify bool, timeout time.Duration, logger log.Logger) func(uri url.URL, subPath string, params url.Values) (io.ReadCloser, error) { + tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !sslVerify}} + client := http.Client{ + Timeout: timeout, + Transport: tr, + } + sEnc := base64.StdEncoding.EncodeToString([]byte(token)) + return func(uri url.URL, subPath string, params url.Values) (io.ReadCloser, error) { + uri.Path = path.Join(uri.Path, subPath) + uri.RawQuery = params.Encode() + url := uri.String() + + level.Info(logger).Log("msg", "Scraping Wakatime", "path", subPath, "url", url) + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, err + } + + req.Header = map[string][]string{ + "Authorization": {"Basic " + sEnc}, + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + + if !(resp.StatusCode >= 200 && resp.StatusCode < 300) { + resp.Body.Close() + return nil, fmt.Errorf("HTTP status %d", resp.StatusCode) + } + return resp.Body, nil + } +} + +// ReadAndUnmarshal reads the JSON response body and unmarshals the response +func ReadAndUnmarshal(body io.ReadCloser, object interface{}) error { + respBody, readErr := ioutil.ReadAll(body) + + if readErr != nil { + return readErr + } + + var jsonErr error + jsonErr = json.Unmarshal(respBody, &object) + if jsonErr != nil { + return jsonErr + } + + return nil +} diff --git a/collector/goal.go b/collector/goal.go new file mode 100644 index 0000000..340585c --- /dev/null +++ b/collector/goal.go @@ -0,0 +1,138 @@ +/* +Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package collector + +import ( + "io" + "net/url" + "strconv" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + goalCollectorName = "goal" + goalSubsystem = "goal" + goalEndpoint = "goals" +) + +type goalCollector struct { + goalThreshold *prometheus.Desc + goalProgress *prometheus.Desc + uri url.URL + fetchStat func(url.URL, string, url.Values) (io.ReadCloser, error) + logger log.Logger +} + +func init() { + registerCollector(goalCollectorName, defaultEnabled, NewGoalCollector) +} + +// NewGoalCollector returns a new Collector exposing all-time stats. +func NewGoalCollector(in CommonInputs, logger log.Logger) (Collector, error) { + return &goalCollector{ + goalThreshold: prometheus.NewDesc( + prometheus.BuildFQName(namespace, goalSubsystem, "threshold_seconds"), + "The goal as set through the wakatime interface.", + []string{ + "name", "id", "type", "delta", "enabled", + "ignore_zero_days", "inverse", "snoozed", "tweeting", + }, + nil, + ), + goalProgress: prometheus.NewDesc( + prometheus.BuildFQName(namespace, goalSubsystem, "progress_seconds"), + "Progress towards the goal.", + []string{ + "name", "id", "type", "delta", "enabled", + "ignore_zero_days", "inverse", "snoozed", "tweeting", + }, + nil, + ), + uri: in.URI, + fetchStat: FetchHTTP(in.Token, in.SSLVerify, in.Timeout, logger), + logger: logger, + }, nil +} + +func (c *goalCollector) Update(ch chan<- prometheus.Metric) error { + params := url.Values{} + params.Add("cache", "false") + + body, fetchErr := c.fetchStat(c.uri, goalEndpoint, params) + if fetchErr != nil { + return fetchErr + } + + goalStats := wakatimeGoal{} + if err := ReadAndUnmarshal(body, &goalStats); err != nil { + return err + } + + defer body.Close() + + level.Info(c.logger).Log( + "msg", "Collecting goals from Wakatime", + "total", goalStats.Total, + "pages", goalStats.TotalPages, + ) + for i, data := range goalStats.Data { + // the last element should be the most recent data + currentChartData := data.ChartData[len(data.ChartData)-1] + + level.Info(c.logger).Log( + "msg", "Collecting goal from Wakatime", + "obj", i, + "start", currentChartData.Range.Start, + "end", currentChartData.Range.End, + "text", currentChartData.Range.Text, + ) + + ch <- prometheus.MustNewConstMetric( + c.goalThreshold, + prometheus.GaugeValue, + float64(currentChartData.GoalSeconds), + data.Title, + data.ID, + data.Type, + data.Delta, + strconv.FormatBool(data.IsEnabled), + strconv.FormatBool(data.IgnoreZeroDays), + strconv.FormatBool(data.IsInverse), + strconv.FormatBool(data.IsSnoozed), + strconv.FormatBool(data.IsTweeting), + ) + + ch <- prometheus.MustNewConstMetric( + c.goalProgress, + prometheus.CounterValue, + currentChartData.ActualSeconds, + data.Title, + data.ID, + data.Type, + data.Delta, + strconv.FormatBool(data.IsEnabled), + strconv.FormatBool(data.IgnoreZeroDays), + strconv.FormatBool(data.IsInverse), + strconv.FormatBool(data.IsSnoozed), + strconv.FormatBool(data.IsTweeting), + ) + } + + return nil +} diff --git a/collectors/goal/types.go b/collector/goal_types.go similarity index 78% rename from collectors/goal/types.go rename to collector/goal_types.go index e031aa2..9f04a73 100644 --- a/collectors/goal/types.go +++ b/collector/goal_types.go @@ -1,23 +1,19 @@ /* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) +Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. +http://www.apache.org/licenses/LICENSE-2.0 -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ -package goal +package collector import ( "time" diff --git a/collector/leader.go b/collector/leader.go new file mode 100644 index 0000000..c37d6fe --- /dev/null +++ b/collector/leader.go @@ -0,0 +1,87 @@ +/* +Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package collector + +import ( + "io" + "net/url" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + leaderCollectorName = "leader" + leaderSubsystem = "leaderboard" + leaderEndpoint = "leaders" +) + +type leaderCollector struct { + rank *prometheus.Desc + uri url.URL + fetchStat func(url.URL, string, url.Values) (io.ReadCloser, error) + logger log.Logger +} + +func init() { + registerCollector(leaderCollectorName, defaultEnabled, NewLeaderCollector) +} + +// NewLeaderCollector returns a new Collector exposing all-time stats. +func NewLeaderCollector(in CommonInputs, logger log.Logger) (Collector, error) { + return &leaderCollector{ + rank: prometheus.NewDesc( + prometheus.BuildFQName(namespace, leaderSubsystem, "rank"), + "Current rank of the user.", + nil, nil, + ), + uri: in.BaseURI, + fetchStat: FetchHTTP(in.Token, in.SSLVerify, in.Timeout, logger), + logger: logger, + }, nil +} + +func (c *leaderCollector) Update(ch chan<- prometheus.Metric) error { + params := url.Values{} + params.Add("cache", "false") + + body, fetchErr := c.fetchStat(c.uri, leaderEndpoint, params) + if fetchErr != nil { + return fetchErr + } + + leaderStats := wakatimeLeader{} + if err := ReadAndUnmarshal(body, &leaderStats); err != nil { + return err + } + + defer body.Close() + + level.Info(c.logger).Log( + "msg", "Collecting rank from Wakatime", + "page", leaderStats.Page, + "updated", leaderStats.ModifiedAt, + ) + + ch <- prometheus.MustNewConstMetric( + c.rank, + prometheus.GaugeValue, + float64(leaderStats.CurrentUser.Rank), + ) + + return nil +} diff --git a/collectors/leader/types.go b/collector/leader_types.go similarity index 77% rename from collectors/leader/types.go rename to collector/leader_types.go index 2ce4319..a506ac4 100644 --- a/collectors/leader/types.go +++ b/collector/leader_types.go @@ -1,23 +1,19 @@ /* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) +Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. +http://www.apache.org/licenses/LICENSE-2.0 -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ -package leader +package collector import ( "time" diff --git a/collector/summary.go b/collector/summary.go new file mode 100644 index 0000000..7159f65 --- /dev/null +++ b/collector/summary.go @@ -0,0 +1,190 @@ +/* +Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package collector + +import ( + "io" + "net/url" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + summaryCollectorName = "summary" + summaryMetricName = "seconds_total" + summaryEndpoint = "summaries" +) + +type summaryCollector struct { + total *prometheus.Desc + language *prometheus.Desc + operatingSystem *prometheus.Desc + machine *prometheus.Desc + editor *prometheus.Desc + project *prometheus.Desc + category *prometheus.Desc + uri url.URL + fetchStat func(url.URL, string, url.Values) (io.ReadCloser, error) + logger log.Logger +} + +func init() { + registerCollector(summaryCollectorName, defaultEnabled, NewSummaryCollector) +} + +// NewSummaryCollector returns a new Collector exposing all-time stats. +func NewSummaryCollector(in CommonInputs, logger log.Logger) (Collector, error) { + return &summaryCollector{ + total: prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", summaryMetricName), + "Total seconds.", + nil, nil, + ), + language: prometheus.NewDesc( + prometheus.BuildFQName(namespace, "language", summaryMetricName), + "Total seconds for each language.", + []string{"name"}, nil, + ), + operatingSystem: prometheus.NewDesc( + prometheus.BuildFQName(namespace, "operating_system", summaryMetricName), + "Total seconds for each operating system.", + []string{"name"}, nil, + ), + machine: prometheus.NewDesc( + prometheus.BuildFQName(namespace, "machine", summaryMetricName), + "Total seconds for each machine.", + []string{"name", "id"}, nil, + ), + editor: prometheus.NewDesc( + prometheus.BuildFQName(namespace, "editor", summaryMetricName), + "Total seconds for each editor.", + []string{"name"}, nil, + ), + project: prometheus.NewDesc( + prometheus.BuildFQName(namespace, "project", summaryMetricName), + "Total seconds for each project.", + []string{"name"}, nil, + ), + category: prometheus.NewDesc( + prometheus.BuildFQName(namespace, "category", summaryMetricName), + "Total seconds for each category.", + []string{"name"}, nil, + ), + uri: in.URI, + fetchStat: FetchHTTP(in.Token, in.SSLVerify, in.Timeout, logger), + logger: logger, + }, nil +} + +func (c *summaryCollector) Update(ch chan<- prometheus.Metric) error { + params := url.Values{} + params.Add("start", "today") + params.Add("end", "today") + params.Add("cache", "false") + + body, fetchErr := c.fetchStat(c.uri, summaryEndpoint, params) + if fetchErr != nil { + return fetchErr + } + + summaryStats := wakatimeSummary{} + if err := ReadAndUnmarshal(body, &summaryStats); err != nil { + return err + } + + defer body.Close() + + for i, data := range summaryStats.Data { + level.Info(c.logger).Log( + "msg", "Collecting summary from Wakatime", + "obj", i, + "start", data.Range.Start.String(), + "end", data.Range.End.String(), + "tz", data.Range.Timezone, + "text", data.Range.Text, + ) + } + + resultLength := len(summaryStats.Data) + if resultLength != 1 { + level.Error(c.logger).Log("msg", "length of results is incorrect", "size", resultLength) + } + todaySummaryStats := summaryStats.Data[0] + + ch <- prometheus.MustNewConstMetric( + c.total, + prometheus.CounterValue, + todaySummaryStats.GrandTotal.TotalSeconds, + ) + + for _, lang := range todaySummaryStats.Languages { + ch <- prometheus.MustNewConstMetric( + c.language, + prometheus.CounterValue, + lang.TotalSeconds, + lang.Name, + ) + } + + for _, os := range todaySummaryStats.OperatingSystems { + ch <- prometheus.MustNewConstMetric( + c.operatingSystem, + prometheus.CounterValue, + os.TotalSeconds, + os.Name, + ) + } + + for _, machine := range todaySummaryStats.Machines { + ch <- prometheus.MustNewConstMetric( + c.machine, + prometheus.CounterValue, + machine.TotalSeconds, + machine.Name, machine.MachineNameID, + ) + } + + for _, editor := range todaySummaryStats.Editors { + ch <- prometheus.MustNewConstMetric( + c.editor, + prometheus.CounterValue, + editor.TotalSeconds, + editor.Name, + ) + } + + for _, project := range todaySummaryStats.Projects { + ch <- prometheus.MustNewConstMetric( + c.project, + prometheus.CounterValue, + project.TotalSeconds, + project.Name, + ) + } + + for _, category := range todaySummaryStats.Categories { + ch <- prometheus.MustNewConstMetric( + c.category, + prometheus.CounterValue, + category.TotalSeconds, + category.Name, + ) + } + + return nil +} diff --git a/collectors/summary/types.go b/collector/summary_types.go similarity index 81% rename from collectors/summary/types.go rename to collector/summary_types.go index aaad96c..d70a984 100644 --- a/collectors/summary/types.go +++ b/collector/summary_types.go @@ -1,23 +1,19 @@ /* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) +Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. +http://www.apache.org/licenses/LICENSE-2.0 -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ -package summary +package collector import ( "time" diff --git a/collectors/alltime/main.go b/collectors/alltime/main.go deleted file mode 100644 index 1cdb84c..0000000 --- a/collectors/alltime/main.go +++ /dev/null @@ -1,130 +0,0 @@ -/* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -package alltime - -import ( - "errors" - "net/url" - "time" - - exporter "github.com/MacroPower/wakatime_exporter/lib" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/log/level" - "github.com/prometheus/client_golang/prometheus" -) - -const ( - subsystem = "alltime" - endpoint = "all_time_since_today" -) - -var ( - wakaMetrics = exporter.Metrics{ - "total": exporter.NewWakaMetric("cumulative_seconds_total", "Total seconds (all time).", prometheus.CounterValue, nil, nil), - } -) - -// Exporter is the local definition of Exporter -type Exporter exporter.Exporter - -// NewExporter creates the AllTime exporter -func NewExporter(baseURI *url.URL, user string, token string, sslVerify bool, timeout time.Duration, logger log.Logger) *Exporter { - fetchStat := exporter.FetchHTTP(token, sslVerify, timeout, logger) - defaultMetrics := exporter.DefaultMetrics(subsystem) - - return &Exporter{ - URI: baseURI, - Endpoint: endpoint, - Subsystem: subsystem, - User: user, - FetchStat: fetchStat, - DefaultMetrics: defaultMetrics, - ExportMetric: exporter.ExportMetric, - Logger: logger, - } -} - -// Describe describes all the metrics ever exported by the wakatime exporter. It -// implements prometheus.Collector. -func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - for _, m := range wakaMetrics { - ch <- m.Desc - } - - ch <- e.DefaultMetrics.Up.Desc() - ch <- e.DefaultMetrics.TotalScrapes.Desc() - ch <- e.DefaultMetrics.QueryFailures.Desc() -} - -// Collect all the metrics. -func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - e.Mutex.Lock() // To protect metrics from concurrent collects. - defer e.Mutex.Unlock() - - err := e.scrape(ch) - up := float64(1) - if err != nil { - up = float64(0) - e.DefaultMetrics.QueryFailures.Inc() - level.Error(e.Logger).Log("msg", "Can't scrape wakatime", "subsystem", subsystem, "err", err) - } - e.DefaultMetrics.Up.Set(up) - - ch <- e.DefaultMetrics.Up - ch <- e.DefaultMetrics.TotalScrapes - ch <- e.DefaultMetrics.QueryFailures -} - -func (e *Exporter) scrape(ch chan<- prometheus.Metric) error { - level.Debug(e.Logger).Log("msg", "Starting scrape") - - e.DefaultMetrics.TotalScrapes.Inc() - - userURL := exporter.UserPath(e.URI, e.User) - - params := url.Values{} - params.Add("cache", "false") - - body, fetchErr := e.FetchStat(userURL, endpoint, params) - if fetchErr != nil { - return fetchErr - } - - alltimeStats := wakatimeAlltime{} - err := exporter.ReadAndUnmarshal(body, &alltimeStats) - if err != nil { - return err - } - - defer body.Close() - - level.Info(e.Logger).Log( - "msg", "Collecting all time from Wakatime", - "IsUpToDate", alltimeStats.Data.IsUpToDate, - ) - if alltimeStats.Data.IsUpToDate == true { - e.ExportMetric(wakaMetrics["total"], ch, alltimeStats.Data.TotalSeconds) - } else { - return errors.New("skipped scrape of alltime metrics because they were not up to date") - } - - return nil -} diff --git a/collectors/alltime/types.go b/collectors/alltime/types.go deleted file mode 100644 index 2bdd658..0000000 --- a/collectors/alltime/types.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -package alltime - -type wakatimeAlltime struct { - Data struct { - IsUpToDate bool `json:"is_up_to_date"` - Text string `json:"text"` - TotalSeconds float64 `json:"total_seconds"` - } `json:"data"` -} diff --git a/collectors/goal/main.go b/collectors/goal/main.go deleted file mode 100644 index 47080c0..0000000 --- a/collectors/goal/main.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -package goal - -import ( - "net/url" - "time" - - exporter "github.com/MacroPower/wakatime_exporter/lib" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/log/level" - "github.com/prometheus/client_golang/prometheus" -) - -const ( - subsystem = "goal" - endpoint = "goals" -) - -var ( - wakaMetrics = exporter.Metrics{ - "goal": exporter.NewWakaMetric("goal_seconds", "The goal.", prometheus.GaugeValue, []string{"name", "id", "type", "delta"}, nil), - "goal_progress": exporter.NewWakaMetric("goal_progress_seconds_total", "Progress towards the goal.", prometheus.CounterValue, []string{"name", "id", "type", "delta"}, nil), - "goal_info": exporter.NewWakaMetric("goal_info", "Information about the goal.", prometheus.GaugeValue, []string{"name", "id", "ignore_zero_days", "is_enabled", "is_inverse", "is_snoozed", "is_tweeting"}, nil), - } -) - -// Exporter is the local definition of Exporter -type Exporter exporter.Exporter - -// NewExporter creates the Goal exporter -func NewExporter(baseURI *url.URL, user string, token string, sslVerify bool, timeout time.Duration, logger log.Logger) *Exporter { - fetchStat := exporter.FetchHTTP(token, sslVerify, timeout, logger) - defaultMetrics := exporter.DefaultMetrics(subsystem) - - return &Exporter{ - URI: baseURI, - Endpoint: endpoint, - Subsystem: subsystem, - User: user, - FetchStat: fetchStat, - DefaultMetrics: defaultMetrics, - ExportMetric: exporter.ExportMetric, - Logger: logger, - } -} - -// Describe describes all the metrics ever exported by the wakatime exporter. It -// implements prometheus.Collector. -func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - for _, m := range wakaMetrics { - ch <- m.Desc - } - - ch <- e.DefaultMetrics.Up.Desc() - ch <- e.DefaultMetrics.TotalScrapes.Desc() - ch <- e.DefaultMetrics.QueryFailures.Desc() -} - -// Collect all the metrics. -func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - e.Mutex.Lock() // To protect metrics from concurrent collects. - defer e.Mutex.Unlock() - - err := e.scrape(ch) - up := float64(1) - if err != nil { - up = float64(0) - e.DefaultMetrics.QueryFailures.Inc() - level.Error(e.Logger).Log("msg", "Can't scrape wakatime", "subsystem", subsystem, "err", err) - } - e.DefaultMetrics.Up.Set(up) - - ch <- e.DefaultMetrics.Up - ch <- e.DefaultMetrics.TotalScrapes - ch <- e.DefaultMetrics.QueryFailures -} - -func (e *Exporter) scrape(ch chan<- prometheus.Metric) error { - level.Debug(e.Logger).Log("msg", "Starting scrape") - - e.DefaultMetrics.TotalScrapes.Inc() - - userURL := exporter.UserPath(e.URI, e.User) - - params := url.Values{} - params.Add("cache", "false") - - body, fetchErr := e.FetchStat(userURL, endpoint, params) - if fetchErr != nil { - return fetchErr - } - - goalStats := wakatimeGoal{} - err := exporter.ReadAndUnmarshal(body, &goalStats) - if err != nil { - return err - } - - defer body.Close() - - level.Info(e.Logger).Log( - "msg", "Collecting goals from Wakatime", - "total", goalStats.Total, - "pages", goalStats.TotalPages, - ) - for i, data := range goalStats.Data { - // the last element should be the most recent data - currentChartData := data.ChartData[len(data.ChartData)-1] - - level.Info(e.Logger).Log( - "msg", "Collecting goal from Wakatime", - "obj", i, - "start", currentChartData.Range.Start, - "end", currentChartData.Range.End, - "text", currentChartData.Range.Text, - ) - - e.ExportMetric( - wakaMetrics["goal_progress"], ch, currentChartData.ActualSeconds, - data.Title, data.ID, data.Type, data.Delta, - ) - e.ExportMetric( - wakaMetrics["goal"], ch, float64(currentChartData.GoalSeconds), - data.Title, data.ID, data.Type, data.Delta, - ) - e.ExportMetric( - wakaMetrics["goal_info"], ch, 1, - data.Title, data.ID, - exporter.BoolToBinary(data.IgnoreZeroDays), - exporter.BoolToBinary(data.IsEnabled), - exporter.BoolToBinary(data.IsInverse), - exporter.BoolToBinary(data.IsSnoozed), - exporter.BoolToBinary(data.IsTweeting), - ) - } - - return nil -} diff --git a/collectors/leader/main.go b/collectors/leader/main.go deleted file mode 100644 index 792ef90..0000000 --- a/collectors/leader/main.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -package leader - -import ( - "net/url" - "time" - - exporter "github.com/MacroPower/wakatime_exporter/lib" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/log/level" - "github.com/prometheus/client_golang/prometheus" -) - -const ( - subsystem = "leader" - endpoint = "leaders" -) - -var ( - wakaMetrics = exporter.Metrics{ - "rank": exporter.NewWakaMetric("rank", "Current rank of the user.", prometheus.GaugeValue, nil, nil), - } -) - -// Exporter is the local definition of Exporter -type Exporter exporter.Exporter - -// NewExporter creates the Leader exporter -func NewExporter(baseURI *url.URL, user string, token string, sslVerify bool, timeout time.Duration, logger log.Logger) *Exporter { - fetchStat := exporter.FetchHTTP(token, sslVerify, timeout, logger) - defaultMetrics := exporter.DefaultMetrics(subsystem) - - return &Exporter{ - URI: baseURI, - Endpoint: endpoint, - Subsystem: subsystem, - User: user, - FetchStat: fetchStat, - DefaultMetrics: defaultMetrics, - ExportMetric: exporter.ExportMetric, - Logger: logger, - } -} - -// Describe describes all the metrics ever exported by the wakatime exporter. It -// implements prometheus.Collector. -func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - for _, m := range wakaMetrics { - ch <- m.Desc - } - - ch <- e.DefaultMetrics.Up.Desc() - ch <- e.DefaultMetrics.TotalScrapes.Desc() - ch <- e.DefaultMetrics.QueryFailures.Desc() -} - -// Collect all the metrics. -func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - e.Mutex.Lock() // To protect metrics from concurrent collects. - defer e.Mutex.Unlock() - - err := e.scrape(ch) - up := float64(1) - if err != nil { - up = float64(0) - e.DefaultMetrics.QueryFailures.Inc() - level.Error(e.Logger).Log("msg", "Can't scrape wakatime", "subsystem", subsystem, "err", err) - } - e.DefaultMetrics.Up.Set(up) - - ch <- e.DefaultMetrics.Up - ch <- e.DefaultMetrics.TotalScrapes - ch <- e.DefaultMetrics.QueryFailures -} - -func (e *Exporter) scrape(ch chan<- prometheus.Metric) error { - level.Debug(e.Logger).Log("msg", "Starting scrape") - - e.DefaultMetrics.TotalScrapes.Inc() - - params := url.Values{} - params.Add("cache", "false") - - body, fetchErr := e.FetchStat(*e.URI, endpoint, params) - if fetchErr != nil { - return fetchErr - } - - leaderStats := wakatimeLeader{} - err := exporter.ReadAndUnmarshal(body, &leaderStats) - if err != nil { - return err - } - - defer body.Close() - - level.Info(e.Logger).Log( - "msg", "Collecting rank from Wakatime", - "page", leaderStats.Page, - "updated", leaderStats.ModifiedAt, - ) - currentRank := float64(leaderStats.CurrentUser.Rank) - e.ExportMetric(wakaMetrics["rank"], ch, currentRank) - - return nil -} diff --git a/collectors/summary/main.go b/collectors/summary/main.go deleted file mode 100644 index 2ed5644..0000000 --- a/collectors/summary/main.go +++ /dev/null @@ -1,170 +0,0 @@ -/* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -package summary - -import ( - "net/url" - "time" - - exporter "github.com/MacroPower/wakatime_exporter/lib" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/log/level" - "github.com/prometheus/client_golang/prometheus" -) - -const ( - subsystem = "summary" - endpoint = "summaries" -) - -var ( - wakaMetrics = exporter.Metrics{ - "total": exporter.NewWakaMetric("seconds_total", "Total seconds.", prometheus.CounterValue, nil, nil), - "language": exporter.NewWakaMetric("language_seconds_total", "Total seconds for each language.", prometheus.CounterValue, []string{"name"}, nil), - "operating_system": exporter.NewWakaMetric("operating_system_seconds_total", "Total seconds for each operating system.", prometheus.CounterValue, []string{"name"}, nil), - "machine": exporter.NewWakaMetric("machine_seconds_total", "Total seconds for each machine.", prometheus.CounterValue, []string{"name", "id"}, nil), - "editor": exporter.NewWakaMetric("editor_seconds_total", "Total seconds for each editor.", prometheus.CounterValue, []string{"name"}, nil), - "project": exporter.NewWakaMetric("project_seconds_total", "Total seconds for each project.", prometheus.CounterValue, []string{"name"}, nil), - "category": exporter.NewWakaMetric("category_seconds_total", "Total seconds for each category.", prometheus.CounterValue, []string{"name"}, nil), - } -) - -// Exporter is the local definition of Exporter -type Exporter exporter.Exporter - -// NewExporter creates the Summary exporter -func NewExporter(baseURI *url.URL, user string, token string, sslVerify bool, timeout time.Duration, logger log.Logger) *Exporter { - fetchStat := exporter.FetchHTTP(token, sslVerify, timeout, logger) - defaultMetrics := exporter.DefaultMetrics(subsystem) - - return &Exporter{ - URI: baseURI, - Endpoint: endpoint, - Subsystem: subsystem, - User: user, - FetchStat: fetchStat, - DefaultMetrics: defaultMetrics, - ExportMetric: exporter.ExportMetric, - Logger: logger, - } -} - -// Describe describes all the metrics ever exported by the wakatime exporter. It -// implements prometheus.Collector. -func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - for _, m := range wakaMetrics { - ch <- m.Desc - } - - ch <- e.DefaultMetrics.Up.Desc() - ch <- e.DefaultMetrics.TotalScrapes.Desc() - ch <- e.DefaultMetrics.QueryFailures.Desc() -} - -// Collect all the metrics. -func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - e.Mutex.Lock() // To protect metrics from concurrent collects. - defer e.Mutex.Unlock() - - err := e.scrape(ch) - up := float64(1) - if err != nil { - up = float64(0) - e.DefaultMetrics.QueryFailures.Inc() - level.Error(e.Logger).Log("msg", "Can't scrape wakatime", "subsystem", subsystem, "err", err) - } - e.DefaultMetrics.Up.Set(up) - - ch <- e.DefaultMetrics.Up - ch <- e.DefaultMetrics.TotalScrapes - ch <- e.DefaultMetrics.QueryFailures -} - -func (e *Exporter) scrape(ch chan<- prometheus.Metric) error { - level.Debug(e.Logger).Log("msg", "Starting scrape") - - e.DefaultMetrics.TotalScrapes.Inc() - - params := url.Values{} - params.Add("start", "today") - params.Add("end", "today") - params.Add("cache", "false") - - userURL := exporter.UserPath(e.URI, e.User) - - body, fetchErr := e.FetchStat(userURL, endpoint, params) - if fetchErr != nil { - return fetchErr - } - - summaryStats := wakatimeSummary{} - err := exporter.ReadAndUnmarshal(body, &summaryStats) - if err != nil { - return err - } - - defer body.Close() - - for i, data := range summaryStats.Data { - level.Info(e.Logger).Log( - "msg", "Collecting summary from Wakatime", - "obj", i, - "start", data.Range.Start.String(), - "end", data.Range.End.String(), - "tz", data.Range.Timezone, - "text", data.Range.Text, - ) - } - - resultLength := len(summaryStats.Data) - if resultLength != 1 { - level.Error(e.Logger).Log("msg", "length of results is incorrect", "size", resultLength) - } - todaySummaryStats := summaryStats.Data[0] - - e.ExportMetric(wakaMetrics["total"], ch, todaySummaryStats.GrandTotal.TotalSeconds) - - for _, lang := range todaySummaryStats.Languages { - e.ExportMetric(wakaMetrics["language"], ch, lang.TotalSeconds, lang.Name) - } - - for _, os := range todaySummaryStats.OperatingSystems { - e.ExportMetric(wakaMetrics["operating_system"], ch, os.TotalSeconds, os.Name) - } - - for _, machine := range todaySummaryStats.Machines { - e.ExportMetric(wakaMetrics["machine"], ch, machine.TotalSeconds, machine.Name, machine.MachineNameID) - } - - for _, editor := range todaySummaryStats.Editors { - e.ExportMetric(wakaMetrics["editor"], ch, editor.TotalSeconds, editor.Name) - } - - for _, project := range todaySummaryStats.Projects { - e.ExportMetric(wakaMetrics["project"], ch, project.TotalSeconds, project.Name) - } - - for _, category := range todaySummaryStats.Categories { - e.ExportMetric(wakaMetrics["category"], ch, category.TotalSeconds, category.Name) - } - - return nil -} diff --git a/go.mod b/go.mod index 1d3f189..49cc67c 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,6 @@ go 1.14 require ( github.com/go-kit/kit v0.10.0 github.com/prometheus/client_golang v1.7.1 - github.com/prometheus/common v0.10.0 + github.com/prometheus/common v0.13.0 gopkg.in/alecthomas/kingpin.v2 v2.2.6 ) diff --git a/go.sum b/go.sum index 3d5e135..8ba72a2 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -132,15 +134,18 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -171,6 +176,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= @@ -202,6 +208,8 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -224,6 +232,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.13.0 h1:vJlpe9wPgDRM1Z+7Wj3zUUjY1nr6/1jNKyl7llliccg= +github.com/prometheus/common v0.13.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -240,6 +250,7 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -279,6 +290,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -303,6 +315,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -327,8 +340,11 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -394,6 +410,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/lib/types.go b/lib/types.go deleted file mode 100644 index 7d2c3f9..0000000 --- a/lib/types.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -package lib - -import ( - "io" - "net/url" - "sync" - - "github.com/go-kit/kit/log" - "github.com/prometheus/client_golang/prometheus" -) - -// Exporter is a struct for all collector exporters -type Exporter struct { - URI *url.URL - Endpoint, User, Subsystem string - Mutex sync.RWMutex - FetchStat func(url.URL, string, url.Values) (io.ReadCloser, error) - - DefaultMetrics MetricDefaults - ExportMetric func(m MetricInfo, ch chan<- prometheus.Metric, value float64, labels ...string) - Logger log.Logger -} - -// Metrics maps all MetricInfo -type Metrics map[string]MetricInfo - -// MetricInfo contains the metric Desc and Type -type MetricInfo struct { - Desc *prometheus.Desc - Type prometheus.ValueType -} - -// MetricDefaults contains the default metrics exported by each collector -type MetricDefaults struct { - Up prometheus.Gauge - TotalScrapes prometheus.Counter - QueryFailures prometheus.Counter -} diff --git a/lib/utils.go b/lib/utils.go deleted file mode 100644 index cf86ead..0000000 --- a/lib/utils.go +++ /dev/null @@ -1,155 +0,0 @@ -/* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -package lib - -import ( - "crypto/tls" - b64 "encoding/base64" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "path" - "time" - - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/log/level" - "github.com/prometheus/client_golang/prometheus" -) - -const ( - namespace = "wakatime" -) - -// NewWakaMetric creates a MetricInfo struct containing metric Desc and Type -func NewWakaMetric(metricName string, docString string, t prometheus.ValueType, variableLabels []string, constLabels prometheus.Labels) MetricInfo { - return MetricInfo{ - Desc: prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", metricName), - docString, - variableLabels, - constLabels, - ), - Type: t, - } -} - -// BoolToBinary converts booleans to "0" or "1" -func BoolToBinary(b bool) string { - if b { - return "1" - } - return "0" -} - -// FetchHTTP is a generic fetch method for Wakatime API endpoints -func FetchHTTP(token string, sslVerify bool, timeout time.Duration, logger log.Logger) func(uri url.URL, subPath string, params url.Values) (io.ReadCloser, error) { - tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !sslVerify}} - client := http.Client{ - Timeout: timeout, - Transport: tr, - } - sEnc := b64.StdEncoding.EncodeToString([]byte(token)) - return func(uri url.URL, subPath string, params url.Values) (io.ReadCloser, error) { - - uri.Path = path.Join(uri.Path, subPath) - uri.RawQuery = params.Encode() - url := uri.String() - - level.Info(logger).Log("msg", "Scraping Wakatime", "path", subPath, "url", url) - - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - return nil, err - } - - req.Header = map[string][]string{ - "Authorization": {"Basic " + sEnc}, - } - - resp, err := client.Do(req) - if err != nil { - return nil, err - } - - if !(resp.StatusCode >= 200 && resp.StatusCode < 300) { - resp.Body.Close() - return nil, fmt.Errorf("HTTP status %d", resp.StatusCode) - } - return resp.Body, nil - } -} - -// ReadAndUnmarshal reads the JSON response body and unmarshals the response -func ReadAndUnmarshal(body io.ReadCloser, object interface{}) error { - respBody, readErr := ioutil.ReadAll(body) - - if readErr != nil { - return readErr - } - - var jsonErr error - jsonErr = json.Unmarshal(respBody, &object) - if jsonErr != nil { - return jsonErr - } - - return nil -} - -// UserPath appends the User path to a given URL -func UserPath(uri *url.URL, user string) url.URL { - userURL := *uri - userPath := path.Join(userURL.Path, "users", user) - - userURL.Path = userPath - return userURL -} - -// DefaultMetrics returns MetricDefaults -func DefaultMetrics(subsystem string) MetricDefaults { - return MetricDefaults{ - Up: prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Subsystem: subsystem, - Name: "up", - Help: "Was the last scrape of wakatime successful.", - }), - TotalScrapes: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Subsystem: subsystem, - Name: "exporter_scrapes_total", - Help: "Current total wakatime scrapes.", - }), - QueryFailures: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Subsystem: subsystem, - Name: "exporter_query_failures_total", - Help: "Number of errors.", - }), - } -} - -// ExportMetric wraps MustNewConstMetric -func ExportMetric(m MetricInfo, ch chan<- prometheus.Metric, value float64, labels ...string) { - ch <- prometheus.MustNewConstMetric(m.Desc, m.Type, value, labels...) -} diff --git a/main.go b/main.go index 44bdd4f..9f1cdab 100644 --- a/main.go +++ b/main.go @@ -1,20 +1,16 @@ /* -Wakatime Exporter for Prometheus -Copyright (C) 2020 Jacob Colvin (MacroPower) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ package main @@ -23,30 +19,72 @@ import ( "net/http" "net/url" "os" + "path" "github.com/go-kit/kit/log/level" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/promlog" "github.com/prometheus/common/promlog/flag" "github.com/prometheus/common/version" "gopkg.in/alecthomas/kingpin.v2" - alltime "github.com/MacroPower/wakatime_exporter/collectors/alltime" - goal "github.com/MacroPower/wakatime_exporter/collectors/goal" - leader "github.com/MacroPower/wakatime_exporter/collectors/leader" - summary "github.com/MacroPower/wakatime_exporter/collectors/summary" + "github.com/MacroPower/wakatime_exporter/collector" ) +// UserPath appends the User path to a given URL +func UserPath(uri *url.URL, user string) url.URL { + userURL := *uri + userPath := path.Join(userURL.Path, "users", user) + + userURL.Path = userPath + return userURL +} + func main() { var ( - listenAddress = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9212").Envar("WAKA_LISTEN_ADDR").String() - metricsPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").Envar("WAKA_METRICS_PATH").String() - wakaScrapeURI = kingpin.Flag("wakatime.scrape-uri", "Base path to query for Wakatime data.").Default("https://wakatime.com/api/v1").Envar("WAKA_SCRAPE_URI").String() - wakaUser = kingpin.Flag("wakatime.user", "User to query for Wakatime data.").Default("current").Envar("WAKA_USER").String() - wakaToken = kingpin.Flag("wakatime.api-key", "Token to use when getting stats from Wakatime.").Required().Envar("WAKA_API_KEY").String() - wakaTimeout = kingpin.Flag("wakatime.timeout", "Timeout for trying to get stats from Wakatime.").Default("5s").Envar("WAKA_TIMEOUT").Duration() - wakaSSLVerify = kingpin.Flag("wakatime.ssl-verify", "Flag that enables SSL certificate verification for the scrape URI.").Default("true").Envar("WAKA_SSL_VERIFY").Bool() + disableDefaultCollectors = kingpin.Flag( + "collector.disable-defaults", + "Set all collectors to disabled by default.", + ).Default("false").Envar("WAKA_DISABLE_DEFAULT_COLLECTORS").Bool() + + listenAddress = kingpin.Flag( + "web.listen-address", + "Address to listen on for web interface and metrics.", + ).Default(":9212").Envar("WAKA_LISTEN_ADDRESS").String() + + metricsPath = kingpin.Flag( + "web.metrics-path", + "Path under which to expose metrics.", + ).Default("/metrics").Envar("WAKA_METRICS_PATH").String() + + disableExporterMetrics = kingpin.Flag( + "web.disable-exporter-metrics", + "Exclude metrics about the exporter itself (promhttp_*, process_*, go_*).", + ).Default("false").Envar("WAKA_DISABLE_EXPORTER_METRICS").Bool() + + wakaScrapeURI = kingpin.Flag( + "wakatime.scrape-uri", + "Base path to query for Wakatime data.", + ).Default("https://wakatime.com/api/v1").Envar("WAKA_SCRAPE_URI").String() + + wakaUser = kingpin.Flag( + "wakatime.user", + "User to query for Wakatime data.", + ).Default("current").Envar("WAKA_USER").String() + + wakaToken = kingpin.Flag( + "wakatime.api-key", + "Token to use when getting stats from Wakatime.", + ).Required().Envar("WAKA_API_KEY").String() + + wakaTimeout = kingpin.Flag( + "wakatime.timeout", + "Timeout for trying to get stats from Wakatime.", + ).Default("5s").Envar("WAKA_TIMEOUT").Duration() + + wakaSSLVerify = kingpin.Flag( + "wakatime.ssl-verify", + "Flag that enables SSL certificate verification for the scrape URI.", + ).Default("true").Envar("WAKA_SSL_VERIFY").Bool() ) promlogConfig := &promlog.Config{} @@ -56,8 +94,11 @@ func main() { kingpin.Parse() logger := promlog.New(promlogConfig) + if *disableDefaultCollectors { + collector.DisableDefaultCollectors() + } level.Info(logger).Log("msg", "Starting wakatime_exporter", "version", version.Info()) - level.Info(logger).Log("msg", "Build context", "context", version.BuildContext()) + level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext()) wakaBaseURI, err := url.Parse(*wakaScrapeURI) if err != nil { @@ -65,25 +106,24 @@ func main() { os.Exit(1) } - exporter := version.NewCollector("wakatime_exporter") - summaryExporter := summary.NewExporter(wakaBaseURI, *wakaUser, *wakaToken, *wakaSSLVerify, *wakaTimeout, logger) - leaderExporter := leader.NewExporter(wakaBaseURI, *wakaUser, *wakaToken, *wakaSSLVerify, *wakaTimeout, logger) - goalExporter := goal.NewExporter(wakaBaseURI, *wakaUser, *wakaToken, *wakaSSLVerify, *wakaTimeout, logger) - alltimeExporter := alltime.NewExporter(wakaBaseURI, *wakaUser, *wakaToken, *wakaSSLVerify, *wakaTimeout, logger) - - prometheus.MustRegister(exporter, summaryExporter, leaderExporter, goalExporter, alltimeExporter) - - level.Info(logger).Log("msg", "Listening on address", "address", *listenAddress) - http.Handle(*metricsPath, promhttp.Handler()) + http.Handle(*metricsPath, newHandler(collector.CommonInputs{ + BaseURI: *wakaBaseURI, + URI: UserPath(wakaBaseURI, *wakaUser), + Token: *wakaToken, + SSLVerify: *wakaSSLVerify, + Timeout: *wakaTimeout, + }, !*disableExporterMetrics, logger)) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` - Wakatime Exporter - -

Wakatime Exporter

-

Metrics

- - `)) + Wakatime Exporter + +

Wakatime Exporter

+

Metrics

+ + `)) }) + + level.Info(logger).Log("msg", "Listening on", "address", *listenAddress) if err := http.ListenAndServe(*listenAddress, nil); err != nil { level.Error(logger).Log("msg", "Error starting HTTP server", "err", err) os.Exit(1) diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..067558a --- /dev/null +++ b/utils.go @@ -0,0 +1,132 @@ +/* +Copyright 2015 The Prometheus Authors +Modifications Copyright 2020 Jacob Colvin (MacroPower) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "net/http" + "sort" + + "github.com/MacroPower/wakatime_exporter/collector" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prometheus/common/version" +) + +// handler wraps an unfiltered http.Handler but uses a filtered handler, +// created on the fly, if filtering is requested. Create instances with +// newHandler. +type handler struct { + unfilteredHandler http.Handler + // exporterMetricsRegistry is a separate registry for the metrics about + // the exporter itself. + exporterMetricsRegistry *prometheus.Registry + includeExporterMetrics bool + commonInputs collector.CommonInputs + logger log.Logger +} + +func newHandler(commonInputs collector.CommonInputs, includeExporterMetrics bool, logger log.Logger) *handler { + h := &handler{ + exporterMetricsRegistry: prometheus.NewRegistry(), + includeExporterMetrics: includeExporterMetrics, + commonInputs: commonInputs, + logger: logger, + } + if h.includeExporterMetrics { + h.exporterMetricsRegistry.MustRegister( + prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}), + prometheus.NewGoCollector(), + ) + } + if innerHandler, err := h.innerHandler(); err != nil { + panic(fmt.Sprintf("Couldn't create metrics handler: %s", err)) + } else { + h.unfilteredHandler = innerHandler + } + return h +} + +// ServeHTTP implements http.Handler. +func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + filters := r.URL.Query()["collect[]"] + level.Debug(h.logger).Log("msg", "collect query:", "filters", filters) + + if len(filters) == 0 { + // No filters, use the prepared unfiltered handler. + h.unfilteredHandler.ServeHTTP(w, r) + return + } + // To serve filtered metrics, we create a filtering handler on the fly. + filteredHandler, err := h.innerHandler(filters...) + if err != nil { + level.Warn(h.logger).Log("msg", "Couldn't create filtered metrics handler:", "err", err) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(fmt.Sprintf("Couldn't create filtered metrics handler: %s", err))) + return + } + filteredHandler.ServeHTTP(w, r) +} + +// innerHandler is used to create both the one unfiltered http.Handler to be +// wrapped by the outer handler and also the filtered handlers created on the +// fly. The former is accomplished by calling innerHandler without any arguments +// (in which case it will log all the collectors enabled via command-line +// flags). +func (h *handler) innerHandler(filters ...string) (http.Handler, error) { + nc, err := collector.NewWakaCollector(h.commonInputs, h.logger, filters...) + if err != nil { + return nil, fmt.Errorf("couldn't create collector: %s", err) + } + + // Only log the creation of an unfiltered handler, which should happen + // only once upon startup. + if len(filters) == 0 { + level.Info(h.logger).Log("msg", "Enabled collectors") + collectors := []string{} + for n := range nc.Collectors { + collectors = append(collectors, n) + } + sort.Strings(collectors) + for _, c := range collectors { + level.Info(h.logger).Log("collector", c) + } + } + + r := prometheus.NewRegistry() + r.MustRegister(version.NewCollector("wakatime_exporter")) + if err := r.Register(nc); err != nil { + return nil, fmt.Errorf("couldn't register collector: %s", err) + } + handler := promhttp.HandlerFor( + prometheus.Gatherers{h.exporterMetricsRegistry, r}, + promhttp.HandlerOpts{ + ErrorHandling: promhttp.ContinueOnError, + Registry: h.exporterMetricsRegistry, + }, + ) + if h.includeExporterMetrics { + // Note that we have to use h.exporterMetricsRegistry here to + // use the same promhttp metrics for all expositions. + handler = promhttp.InstrumentMetricHandler( + h.exporterMetricsRegistry, handler, + ) + } + return handler, nil +} diff --git a/vendor/github.com/alecthomas/units/bytes.go b/vendor/github.com/alecthomas/units/bytes.go index eaadeb8..61d0ca4 100644 --- a/vendor/github.com/alecthomas/units/bytes.go +++ b/vendor/github.com/alecthomas/units/bytes.go @@ -27,6 +27,7 @@ var ( // ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB // and KiB are both 1024. +// However "kB", which is the correct SI spelling of 1000 Bytes, is rejected. func ParseBase2Bytes(s string) (Base2Bytes, error) { n, err := ParseUnit(s, bytesUnitMap) if err != nil { @@ -68,12 +69,13 @@ func ParseMetricBytes(s string) (MetricBytes, error) { return MetricBytes(n), err } +// TODO: represents 1000B as uppercase "KB", while SI standard requires "kB". func (m MetricBytes) String() string { return ToString(int64(m), 1000, "B", "B") } // ParseStrictBytes supports both iB and B suffixes for base 2 and metric, -// respectively. That is, KiB represents 1024 and KB represents 1000. +// respectively. That is, KiB represents 1024 and kB, KB represent 1000. func ParseStrictBytes(s string) (int64, error) { n, err := ParseUnit(s, bytesUnitMap) if err != nil { diff --git a/vendor/github.com/alecthomas/units/go.mod b/vendor/github.com/alecthomas/units/go.mod index f572173..c7fb91f 100644 --- a/vendor/github.com/alecthomas/units/go.mod +++ b/vendor/github.com/alecthomas/units/go.mod @@ -1 +1,3 @@ module github.com/alecthomas/units + +require github.com/stretchr/testify v1.4.0 diff --git a/vendor/github.com/alecthomas/units/go.sum b/vendor/github.com/alecthomas/units/go.sum new file mode 100644 index 0000000..8fdee58 --- /dev/null +++ b/vendor/github.com/alecthomas/units/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/alecthomas/units/si.go b/vendor/github.com/alecthomas/units/si.go index 8234a9d..99b2fa4 100644 --- a/vendor/github.com/alecthomas/units/si.go +++ b/vendor/github.com/alecthomas/units/si.go @@ -14,13 +14,37 @@ const ( ) func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 { - return map[string]float64{ - shortSuffix: 1, - "K" + suffix: float64(scale), + res := map[string]float64{ + shortSuffix: 1, + // see below for "k" / "K" "M" + suffix: float64(scale * scale), "G" + suffix: float64(scale * scale * scale), "T" + suffix: float64(scale * scale * scale * scale), "P" + suffix: float64(scale * scale * scale * scale * scale), "E" + suffix: float64(scale * scale * scale * scale * scale * scale), } + + // Standard SI prefixes use lowercase "k" for kilo = 1000. + // For compatibility, and to be fool-proof, we accept both "k" and "K" in metric mode. + // + // However, official binary prefixes are always capitalized - "KiB" - + // and we specifically never parse "kB" as 1024B because: + // + // (1) people pedantic enough to use lowercase according to SI unlikely to abuse "k" to mean 1024 :-) + // + // (2) Use of capital K for 1024 was an informal tradition predating IEC prefixes: + // "The binary meaning of the kilobyte for 1024 bytes typically uses the symbol KB, with an + // uppercase letter K." + // -- https://en.wikipedia.org/wiki/Kilobyte#Base_2_(1024_bytes) + // "Capitalization of the letter K became the de facto standard for binary notation, although this + // could not be extended to higher powers, and use of the lowercase k did persist.[13][14][15]" + // -- https://en.wikipedia.org/wiki/Binary_prefix#History + // See also the extensive https://en.wikipedia.org/wiki/Timeline_of_binary_prefixes. + if scale == 1024 { + res["K"+suffix] = float64(scale) + } else { + res["k"+suffix] = float64(scale) + res["K"+suffix] = float64(scale) + } + return res } diff --git a/vendor/github.com/pkg/errors/.travis.yml b/vendor/github.com/pkg/errors/.travis.yml index d4b9266..9159de0 100644 --- a/vendor/github.com/pkg/errors/.travis.yml +++ b/vendor/github.com/pkg/errors/.travis.yml @@ -1,15 +1,10 @@ language: go go_import_path: github.com/pkg/errors go: - - 1.4.x - - 1.5.x - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x - - 1.10.x - 1.11.x + - 1.12.x + - 1.13.x - tip script: - - go test -v ./... + - make check diff --git a/vendor/github.com/pkg/errors/Makefile b/vendor/github.com/pkg/errors/Makefile new file mode 100644 index 0000000..ce9d7cd --- /dev/null +++ b/vendor/github.com/pkg/errors/Makefile @@ -0,0 +1,44 @@ +PKGS := github.com/pkg/errors +SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS)) +GO := go + +check: test vet gofmt misspell unconvert staticcheck ineffassign unparam + +test: + $(GO) test $(PKGS) + +vet: | test + $(GO) vet $(PKGS) + +staticcheck: + $(GO) get honnef.co/go/tools/cmd/staticcheck + staticcheck -checks all $(PKGS) + +misspell: + $(GO) get github.com/client9/misspell/cmd/misspell + misspell \ + -locale GB \ + -error \ + *.md *.go + +unconvert: + $(GO) get github.com/mdempsky/unconvert + unconvert -v $(PKGS) + +ineffassign: + $(GO) get github.com/gordonklaus/ineffassign + find $(SRCDIRS) -name '*.go' | xargs ineffassign + +pedantic: check errcheck + +unparam: + $(GO) get mvdan.cc/unparam + unparam ./... + +errcheck: + $(GO) get github.com/kisielk/errcheck + errcheck $(PKGS) + +gofmt: + @echo Checking code is gofmted + @test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)" diff --git a/vendor/github.com/pkg/errors/README.md b/vendor/github.com/pkg/errors/README.md index 6483ba2..54dfdcb 100644 --- a/vendor/github.com/pkg/errors/README.md +++ b/vendor/github.com/pkg/errors/README.md @@ -41,11 +41,18 @@ default: [Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). +## Roadmap + +With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows: + +- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible) +- 1.0. Final release. + ## Contributing -We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. +Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports. -Before proposing a change, please discuss your change by raising an issue. +Before sending a PR, please discuss your change by raising an issue. ## License diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go index 7421f32..161aea2 100644 --- a/vendor/github.com/pkg/errors/errors.go +++ b/vendor/github.com/pkg/errors/errors.go @@ -82,7 +82,7 @@ // // if err, ok := err.(stackTracer); ok { // for _, f := range err.StackTrace() { -// fmt.Printf("%+s:%d", f) +// fmt.Printf("%+s:%d\n", f, f) // } // } // @@ -159,6 +159,9 @@ type withStack struct { func (w *withStack) Cause() error { return w.error } +// Unwrap provides compatibility for Go 1.13 error chains. +func (w *withStack) Unwrap() error { return w.error } + func (w *withStack) Format(s fmt.State, verb rune) { switch verb { case 'v': @@ -241,6 +244,9 @@ type withMessage struct { func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } func (w *withMessage) Cause() error { return w.cause } +// Unwrap provides compatibility for Go 1.13 error chains. +func (w *withMessage) Unwrap() error { return w.cause } + func (w *withMessage) Format(s fmt.State, verb rune) { switch verb { case 'v': diff --git a/vendor/github.com/pkg/errors/go113.go b/vendor/github.com/pkg/errors/go113.go new file mode 100644 index 0000000..be0d10d --- /dev/null +++ b/vendor/github.com/pkg/errors/go113.go @@ -0,0 +1,38 @@ +// +build go1.13 + +package errors + +import ( + stderrors "errors" +) + +// Is reports whether any error in err's chain matches target. +// +// The chain consists of err itself followed by the sequence of errors obtained by +// repeatedly calling Unwrap. +// +// An error is considered to match a target if it is equal to that target or if +// it implements a method Is(error) bool such that Is(target) returns true. +func Is(err, target error) bool { return stderrors.Is(err, target) } + +// As finds the first error in err's chain that matches target, and if so, sets +// target to that error value and returns true. +// +// The chain consists of err itself followed by the sequence of errors obtained by +// repeatedly calling Unwrap. +// +// An error matches target if the error's concrete value is assignable to the value +// pointed to by target, or if the error has a method As(interface{}) bool such that +// As(target) returns true. In the latter case, the As method is responsible for +// setting target. +// +// As will panic if target is not a non-nil pointer to either a type that implements +// error, or to any interface type. As returns false if err is nil. +func As(err error, target interface{}) bool { return stderrors.As(err, target) } + +// Unwrap returns the result of calling the Unwrap method on err, if err's +// type contains an Unwrap method returning error. +// Otherwise, Unwrap returns nil. +func Unwrap(err error) error { + return stderrors.Unwrap(err) +} diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go index 2874a04..779a834 100644 --- a/vendor/github.com/pkg/errors/stack.go +++ b/vendor/github.com/pkg/errors/stack.go @@ -5,10 +5,13 @@ import ( "io" "path" "runtime" + "strconv" "strings" ) // Frame represents a program counter inside a stack frame. +// For historical reasons if Frame is interpreted as a uintptr +// its value represents the program counter + 1. type Frame uintptr // pc returns the program counter for this frame; @@ -37,6 +40,15 @@ func (f Frame) line() int { return line } +// name returns the name of this function, if known. +func (f Frame) name() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + return fn.Name() +} + // Format formats the frame according to the fmt.Formatter interface. // // %s source file @@ -54,22 +66,16 @@ func (f Frame) Format(s fmt.State, verb rune) { case 's': switch { case s.Flag('+'): - pc := f.pc() - fn := runtime.FuncForPC(pc) - if fn == nil { - io.WriteString(s, "unknown") - } else { - file, _ := fn.FileLine(pc) - fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) - } + io.WriteString(s, f.name()) + io.WriteString(s, "\n\t") + io.WriteString(s, f.file()) default: io.WriteString(s, path.Base(f.file())) } case 'd': - fmt.Fprintf(s, "%d", f.line()) + io.WriteString(s, strconv.Itoa(f.line())) case 'n': - name := runtime.FuncForPC(f.pc()).Name() - io.WriteString(s, funcname(name)) + io.WriteString(s, funcname(f.name())) case 'v': f.Format(s, 's') io.WriteString(s, ":") @@ -77,6 +83,16 @@ func (f Frame) Format(s fmt.State, verb rune) { } } +// MarshalText formats a stacktrace Frame as a text string. The output is the +// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs. +func (f Frame) MarshalText() ([]byte, error) { + name := f.name() + if name == "unknown" { + return []byte(name), nil + } + return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil +} + // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). type StackTrace []Frame @@ -94,16 +110,30 @@ func (st StackTrace) Format(s fmt.State, verb rune) { switch { case s.Flag('+'): for _, f := range st { - fmt.Fprintf(s, "\n%+v", f) + io.WriteString(s, "\n") + f.Format(s, verb) } case s.Flag('#'): fmt.Fprintf(s, "%#v", []Frame(st)) default: - fmt.Fprintf(s, "%v", []Frame(st)) + st.formatSlice(s, verb) } case 's': - fmt.Fprintf(s, "%s", []Frame(st)) + st.formatSlice(s, verb) + } +} + +// formatSlice will format this StackTrace into the given buffer as a slice of +// Frame, only valid when called with '%s' or '%v'. +func (st StackTrace) formatSlice(s fmt.State, verb rune) { + io.WriteString(s, "[") + for i, f := range st { + if i > 0 { + io.WriteString(s, " ") + } + f.Format(s, verb) } + io.WriteString(s, "]") } // stack represents a stack of program counters. diff --git a/vendor/github.com/prometheus/common/expfmt/decode.go b/vendor/github.com/prometheus/common/expfmt/decode.go index c092723..7657f84 100644 --- a/vendor/github.com/prometheus/common/expfmt/decode.go +++ b/vendor/github.com/prometheus/common/expfmt/decode.go @@ -164,7 +164,7 @@ func (sd *SampleDecoder) Decode(s *model.Vector) error { } // ExtractSamples builds a slice of samples from the provided metric -// families. If an error occurrs during sample extraction, it continues to +// families. If an error occurs during sample extraction, it continues to // extract from the remaining metric families. The returned error is the last // error that has occurred. func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) { diff --git a/vendor/github.com/prometheus/common/model/fnv.go b/vendor/github.com/prometheus/common/model/fnv.go index 038fc1c..367afec 100644 --- a/vendor/github.com/prometheus/common/model/fnv.go +++ b/vendor/github.com/prometheus/common/model/fnv.go @@ -20,7 +20,7 @@ const ( prime64 = 1099511628211 ) -// hashNew initializies a new fnv64a hash value. +// hashNew initializes a new fnv64a hash value. func hashNew() uint64 { return offset64 } diff --git a/vendor/github.com/prometheus/common/model/time.go b/vendor/github.com/prometheus/common/model/time.go index 490a024..c40e640 100644 --- a/vendor/github.com/prometheus/common/model/time.go +++ b/vendor/github.com/prometheus/common/model/time.go @@ -181,77 +181,77 @@ func (d *Duration) Type() string { return "duration" } -var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$") +var durationRE = regexp.MustCompile("^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$") // ParseDuration parses a string into a time.Duration, assuming that a year // always has 365d, a week always has 7d, and a day always has 24h. func ParseDuration(durationStr string) (Duration, error) { - // Allow 0 without a unit. - if durationStr == "0" { + switch durationStr { + case "0": + // Allow 0 without a unit. return 0, nil + case "": + return 0, fmt.Errorf("empty duration string") } matches := durationRE.FindStringSubmatch(durationStr) - if len(matches) != 3 { + if matches == nil { return 0, fmt.Errorf("not a valid duration string: %q", durationStr) } - var ( - n, _ = strconv.Atoi(matches[1]) - dur = time.Duration(n) * time.Millisecond - ) - switch unit := matches[2]; unit { - case "y": - dur *= 1000 * 60 * 60 * 24 * 365 - case "w": - dur *= 1000 * 60 * 60 * 24 * 7 - case "d": - dur *= 1000 * 60 * 60 * 24 - case "h": - dur *= 1000 * 60 * 60 - case "m": - dur *= 1000 * 60 - case "s": - dur *= 1000 - case "ms": - // Value already correct - default: - return 0, fmt.Errorf("invalid time unit in duration string: %q", unit) + var dur time.Duration + + // Parse the match at pos `pos` in the regex and use `mult` to turn that + // into ms, then add that value to the total parsed duration. + m := func(pos int, mult time.Duration) { + if matches[pos] == "" { + return + } + n, _ := strconv.Atoi(matches[pos]) + d := time.Duration(n) * time.Millisecond + dur += d * mult } + + m(2, 1000*60*60*24*365) // y + m(4, 1000*60*60*24*7) // w + m(6, 1000*60*60*24) // d + m(8, 1000*60*60) // h + m(10, 1000*60) // m + m(12, 1000) // s + m(14, 1) // ms + return Duration(dur), nil } func (d Duration) String() string { var ( - ms = int64(time.Duration(d) / time.Millisecond) - unit = "ms" + ms = int64(time.Duration(d) / time.Millisecond) + r = "" ) if ms == 0 { return "0s" } - factors := map[string]int64{ - "y": 1000 * 60 * 60 * 24 * 365, - "w": 1000 * 60 * 60 * 24 * 7, - "d": 1000 * 60 * 60 * 24, - "h": 1000 * 60 * 60, - "m": 1000 * 60, - "s": 1000, - "ms": 1, - } - switch int64(0) { - case ms % factors["y"]: - unit = "y" - case ms % factors["w"]: - unit = "w" - case ms % factors["d"]: - unit = "d" - case ms % factors["h"]: - unit = "h" - case ms % factors["m"]: - unit = "m" - case ms % factors["s"]: - unit = "s" + f := func(unit string, mult int64, exact bool) { + if exact && ms%mult != 0 { + return + } + if v := ms / mult; v > 0 { + r += fmt.Sprintf("%d%s", v, unit) + ms -= v * mult + } } - return fmt.Sprintf("%v%v", ms/factors[unit], unit) + + // Only format years and weeks if the remainder is zero, as it is often + // easier to read 90d than 12w6d. + f("y", 1000*60*60*24*365, true) + f("w", 1000*60*60*24*7, true) + + f("d", 1000*60*60*24, false) + f("h", 1000*60*60, false) + f("m", 1000*60, false) + f("s", 1000, false) + f("ms", 1, false) + + return r } // MarshalYAML implements the yaml.Marshaler interface. diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index 780e387..08f8230 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -509,7 +509,7 @@ ccflags="$@" $2 ~ /^CAP_/ || $2 ~ /^ALG_/ || $2 ~ /^FS_(POLICY_FLAGS|KEY_DESC|ENCRYPTION_MODE|[A-Z0-9_]+_KEY_SIZE)/ || - $2 ~ /^FS_IOC_.*(ENCRYPTION|VERITY|GETFLAGS)/ || + $2 ~ /^FS_IOC_.*(ENCRYPTION|VERITY|[GS]ETFLAGS)/ || $2 ~ /^FS_VERITY_/ || $2 ~ /^FSCRYPT_/ || $2 ~ /^GRND_/ || diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 7b7c727..e50e4cb 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -1950,6 +1950,20 @@ func Vmsplice(fd int, iovs []Iovec, flags int) (int, error) { return int(n), nil } +func isGroupMember(gid int) bool { + groups, err := Getgroups() + if err != nil { + return false + } + + for _, g := range groups { + if g == gid { + return true + } + } + return false +} + //sys faccessat(dirfd int, path string, mode uint32) (err error) func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { @@ -2007,7 +2021,7 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { gid = Getgid() } - if uint32(gid) == st.Gid { + if uint32(gid) == st.Gid || isGroupMember(gid) { fmode = (st.Mode >> 3) & 7 } else { fmode = st.Mode & 7 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 8d207b0..11b25f6 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -78,6 +78,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 + FS_IOC_SETFLAGS = 0x40046602 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 F_GETLK = 0xc F_GETLK64 = 0xc diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index c4bf9cb..f92cff6 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -78,6 +78,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 + FS_IOC_SETFLAGS = 0x40086602 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 F_GETLK = 0x5 F_GETLK64 = 0x5 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 0cab052..12bcbf8 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -77,6 +77,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 + FS_IOC_SETFLAGS = 0x40046602 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 F_GETLK = 0xc F_GETLK64 = 0xc diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 370d0a7..8b0e024 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -80,6 +80,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 + FS_IOC_SETFLAGS = 0x40086602 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 F_GETLK = 0x5 F_GETLK64 = 0x5 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index fbf2f31..eeadea9 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -77,6 +77,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 + FS_IOC_SETFLAGS = 0x80046602 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 F_GETLK = 0x21 F_GETLK64 = 0x21 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index 25e74b3..0be6c4c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -77,6 +77,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 + FS_IOC_SETFLAGS = 0x80086602 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 F_GETLK = 0xe F_GETLK64 = 0xe diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index 4ecc0bc..0880b74 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -77,6 +77,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 + FS_IOC_SETFLAGS = 0x80086602 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 F_GETLK = 0xe F_GETLK64 = 0xe diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index dfb8f88..c8a6662 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -77,6 +77,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 + FS_IOC_SETFLAGS = 0x80046602 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 F_GETLK = 0x21 F_GETLK64 = 0x21 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 72d8dad..97aae63 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -77,6 +77,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 + FS_IOC_SETFLAGS = 0x80086602 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 F_GETLK = 0x5 F_GETLK64 = 0xc diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index ca0e7b5..b0c3b06 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -77,6 +77,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 + FS_IOC_SETFLAGS = 0x80086602 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 F_GETLK = 0x5 F_GETLK64 = 0xc diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 147511a..0c05181 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -77,6 +77,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 + FS_IOC_SETFLAGS = 0x40086602 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 F_GETLK = 0x5 F_GETLK64 = 0x5 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 517349d..0b96bd4 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -77,6 +77,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 + FS_IOC_SETFLAGS = 0x40086602 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 F_GETLK = 0x5 F_GETLK64 = 0x5 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index 0948224..bd5c305 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -81,6 +81,7 @@ const ( FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 + FS_IOC_SETFLAGS = 0x80086602 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 F_GETLK = 0x7 F_GETLK64 = 0x7 diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go index 6f79227..b91c2ae 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go @@ -125,9 +125,9 @@ type Statfs_t struct { Owner uint32 Fsid Fsid Charspare [80]int8 - Fstypename [16]int8 - Mntfromname [1024]int8 - Mntonname [1024]int8 + Fstypename [16]byte + Mntfromname [1024]byte + Mntonname [1024]byte } type statfs_freebsd11_t struct { @@ -150,9 +150,9 @@ type statfs_freebsd11_t struct { Owner uint32 Fsid Fsid Charspare [80]int8 - Fstypename [16]int8 - Mntfromname [88]int8 - Mntonname [88]int8 + Fstypename [16]byte + Mntfromname [88]byte + Mntonname [88]byte } type Flock_t struct { diff --git a/vendor/modules.txt b/vendor/modules.txt index f67e2bf..83c3e83 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,7 +1,7 @@ # github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 github.com/alecthomas/template github.com/alecthomas/template/parse -# github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 +# github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d github.com/alecthomas/units # github.com/beorn7/perks v1.0.1 github.com/beorn7/perks/quantile @@ -21,7 +21,7 @@ github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/timestamp # github.com/matttproud/golang_protobuf_extensions v1.0.1 github.com/matttproud/golang_protobuf_extensions/pbutil -# github.com/pkg/errors v0.8.1 +# github.com/pkg/errors v0.9.1 github.com/pkg/errors # github.com/prometheus/client_golang v1.7.1 ## explicit @@ -30,7 +30,7 @@ github.com/prometheus/client_golang/prometheus/internal github.com/prometheus/client_golang/prometheus/promhttp # github.com/prometheus/client_model v0.2.0 github.com/prometheus/client_model/go -# github.com/prometheus/common v0.10.0 +# github.com/prometheus/common v0.13.0 ## explicit github.com/prometheus/common/expfmt github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg @@ -42,7 +42,7 @@ github.com/prometheus/common/version github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util -# golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 +# golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae golang.org/x/sys/internal/unsafeheader golang.org/x/sys/unix golang.org/x/sys/windows