Compare commits

...

132 Commits

Author SHA1 Message Date
Calle Pettersson
517cd3b04b Merge pull request #263 from martinlindhe/linting
Add linting
2019-01-27 21:28:50 +01:00
Calle Pettersson
9daa8c8775 Don't shadow err 2019-01-23 10:02:13 +01:00
Calle Pettersson
e04d3f414d Explicitly set which linters are enabled 2019-01-23 10:01:31 +01:00
Calle Pettersson
4c69ed1610 Lint-fix: errcheck not found on local run(?) 2019-01-23 10:01:31 +01:00
Calle Pettersson
a171401f57 Lint-fix: errcheck. Partial fix to #261 2019-01-23 10:00:52 +01:00
Calle Pettersson
e24e0dc9f5 Lint-fix: Ignore goconst 2019-01-23 09:57:08 +01:00
Calle Pettersson
0eab86c731 Lint-fix: vet. Fixes potential overflow in AD collector 2019-01-23 09:56:24 +01:00
Calle Pettersson
13c68634ce Lint-fix: varcheck 2019-01-23 09:56:24 +01:00
Calle Pettersson
73ad1ba960 Lint-fix: vetshadow 2019-01-23 09:56:24 +01:00
Calle Pettersson
0121fd6471 Lint-fix: Final golint, ignore capitalisation rules 2019-01-23 09:56:24 +01:00
Calle Pettersson
93904954f4 Lint-fix: Dead code. Fixes bug with IIS app white/blacklisting 2019-01-23 09:56:24 +01:00
Calle Pettersson
f2462b26c8 Lint-fix: Package comments 2019-01-23 09:56:24 +01:00
Calle Pettersson
7e05621b26 Move to makefile, do initial excludes 2019-01-23 09:56:24 +01:00
Calle Pettersson
76ddad34b8 Add linting 2019-01-23 09:55:26 +01:00
Calle Pettersson
2053dea3ac Fix log message source (#312) 2019-01-23 02:25:23 +01:00
Calle Pettersson
35b81dcdd0 Change AppVeyor build to cross-compile instead of having two runs (#311) 2019-01-23 02:24:59 +01:00
Calle Pettersson
39b0000514 Merge pull request #303 from monitoring-tools/failed_querying_wp
There was added the checking and logging of query worker processes.
2019-01-20 21:24:14 +01:00
Calle Pettersson
76ec763c42 Merge pull request #307 from giuliov/master
Added an example for textfile collector
2019-01-18 09:10:33 +01:00
Giulio Vian
7ccc47cc51 Added an example for textfile collector
Added a practical example for textfile collector using a Powershell snippet that generates a valid file.
2019-01-16 13:15:57 +00:00
vlamug
ad29ac0792 There was added the checking and logging of query worker processes 2018-12-21 15:16:08 +04:00
Calle Pettersson
d58ce114d9 Merge pull request #299 from hairyhenderson/update-utfbom-dep
textfile collector: updating utfbom dependency and simplifying code
2018-12-12 20:05:44 +01:00
Dave Henderson
5f9dfcc378 textfile collector: updating utfbom dependency and simplifying code
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
2018-12-12 12:30:25 -05:00
Calle Pettersson
f4e5bc3d29 Merge pull request #296 from amitsaha/update_readmelinks
Update main README and docs/README
2018-12-05 07:10:31 +01:00
Amit Saha
f4362c5987 Update README 2018-12-05 11:48:23 +11:00
Amit Saha
f691b48304 Update README 2018-12-05 11:42:30 +11:00
Calle Pettersson
d12d31a17f Merge pull request #294 from hairyhenderson/windows-build-constraints
Adding windows build constraints
2018-11-30 18:57:56 +01:00
Calle Pettersson
48d23cfb12 Merge pull request #295 from h3po/patch-1
Update collector.textfile.md
2018-11-30 18:55:42 +01:00
Daniel
17039b8206 Update collector.textfile.md
Had to read the code to find out why my `.txt` textfiles weren't being read
2018-11-30 13:48:34 +01:00
Calle Pettersson
2993552e19 Merge pull request #293 from hairyhenderson/ensure-utf8-textfiles
textfile collector: Ensure that only UTF8 textfiles are parsed
2018-11-30 07:44:32 +01:00
Dave Henderson
5d4cafc0a1 Adding windows build constraints
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
2018-11-29 19:51:12 -05:00
Dave Henderson
a70c57ffd1 textfile collector: Ensure that only UTF8 textfiles are parsed
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
2018-11-29 16:41:24 -05:00
Kevin Pettijohn
b2cb04834a MSI parameter - EXTRA_FLAGS (#291)
Added the parameter called EXTRA_FLAGS to handle additional unspecified flags within the MSI installer.
2018-11-29 20:14:37 +01:00
Calle Pettersson
f27fdbbbf5 Merge pull request #287 from coalfire/add-console_template
add wmi-overview console template
2018-11-20 09:51:41 +01:00
Noah Birnel
7dda8eba03 add wmi-overview console template 2018-11-19 15:12:22 -08:00
Calle Pettersson
080f80eb26 Fix broken summary table in collector docs (#286) 2018-11-18 22:26:38 +01:00
Calle Pettersson
2766f0e3af Merge pull request #266 from aswhit1/os_memory
Win32_PerfRawData_PerfOS_Memory module
2018-11-18 18:29:42 +01:00
Calle Pettersson
939f4832ee Add docs 2018-11-18 18:26:58 +01:00
Calle Pettersson
9e1d4bbaed Fix naming 2018-11-18 18:26:15 +01:00
aswhit1
8ef341a51c commented out 'Name' variable in struct declaration 2018-11-18 18:26:15 +01:00
aswhit1
1fde8bae5b Win32_PerfRawData_PerfOS_Memory module
Initial commit
2018-11-18 18:26:15 +01:00
Calle Pettersson
48220d825e Merge pull request #285 from hairyhenderson/skip-bom
textfile collector: Skip BOM when present
2018-11-18 17:25:27 +01:00
Calle Pettersson
9ed68ae86c Collector documentation (#113)
* Initial draft of collector documentation

* Add docs for all collectors

* Update main README with links to docs

* Fix link syntax
2018-11-18 17:05:37 +01:00
Dave Henderson
700bbb37c5 textfile collector: Skip BOM when present
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
2018-11-18 10:23:15 -05:00
Calle Pettersson
cb9da1ae22 Merge pull request #256 from martinlindhe/mssql-clean-instance-names
Strip special chars from instance names
2018-08-31 18:16:43 +02:00
Calle Pettersson
7de316af9f Strip special chars from instance names 2018-08-31 17:21:32 +02:00
Calle Pettersson
263ab8c444 Merge pull request #253 from kafecho/wildcard_query_escape
Clarified that the % character has to be escaped
2018-08-28 12:19:36 +02:00
Guillaume Belrose
57449c4768 Clarified that the % character has to be escaped when using the wmi_exporter from the command prompt or batch files. 2018-08-28 08:58:08 +02:00
Calle Pettersson
fe7e5cb4d8 Merge pull request #241 from szook/protect-against-empty-result-set
protect against empty wmi query result sets
2018-08-08 09:14:27 +02:00
Steve Zook
c156f2bcbe protect against emtpy wmi query result sets
If an WMI query were to return an empty result set, trying to access the
first element in the result set would result in a `panic: runtime error:
index out of range` error.

add logic to explicitly check if the result set size is 0 and return an
error rather.

Fixes https://github.com/martinlindhe/wmi_exporter/issues/240
2018-08-07 15:47:39 -04:00
Calle Pettersson
832771b4a2 Merge pull request #238 from bbigras/patch-1
fix typo in exporter.go
2018-08-06 11:41:43 +02:00
Bruno Bigras
16fecfbc67 fix typo in exporter.go 2018-08-05 21:39:43 -04:00
Calle Pettersson
bad1e7f7b0 Merge pull request #237 from szook/fix-mssql-for-non-default-instances
fix mssql for non-default sql instances
2018-08-04 08:33:23 +02:00
Steve Zook
c868c00e89 fix mssql for non-default sql instances
the only sql servers I have access to are using the default
`MSSQLSERVER` instance names.  I was contacted by someone using a
different instance name and it was failing.

Found 2 bugs in the code:

1) I was returning the default `MSSQLSERVER` as an instance even if it wasn't found in the registry

2) learned that the WMI class naming scheme for non-default instances
was not what I had coded. Changed to incorproate new knowledge.
2018-08-03 16:31:19 -04:00
Calle Pettersson
5035e97369 Merge pull request #236 from szook/add-mssql-accessmethod-class
add mssql accessmethod class
2018-08-03 22:16:08 +02:00
Steve Zook
144715e3d2 remove persec from variable names
removed persec from variable names

but when I did that, there was some variable name collisions, so I went
through and name-spaced the class variables.
1
2018-08-03 15:39:26 -04:00
Steve Zook
a20cf1274a add mssql accessmethod class
in the process of trying to build a grafana dashboard with "useful"
mssql metrics, I was looking around for what metrics might be useful and
came across an article of [15 SQL Server Performace Counters to Monitor](https://blogs.sentryone.com/allenwhite/sql-server-performance-counters-to-monitor/)

two of the suggested metrics are provided by the AccessMethod
class, which I was not yet capturing.

So I added metrics for the
Win32_PerfRawData_MSSQLSERVER_SQLServerAccessMethods class.
2018-08-03 13:45:51 -04:00
Calle Pettersson
626a25cd00 Merge pull request #235 from szook/reorder-functions
reorder functions
2018-08-03 16:42:32 +02:00
Calle Pettersson
96dd456bb1 Merge pull request #234 from szook/fix-mssql-metric-name-typo
fix mssql metric name type
2018-08-03 16:27:40 +02:00
Steve Zook
af1b8bf4d0 fix mssql metric name type
found that I had one metric with an extra `s` in `_genstatss_`, making it
inconsistent with it's siblings.
2018-08-03 09:52:15 -04:00
Steve Zook
d83615a818 reorder functions
so that the two functions that list available child collectors are next
to each other - should make less likely to not miss editing one of those
when adding additional wmi classes

also removed the 'filter' from the `mssqlFilterAvailableClassCollectors`
function as it's not filtering anyting.
2018-08-03 09:48:32 -04:00
szook
fe4c61a70e Add mssql collector (#230)
add mssql collector
2018-08-02 08:42:02 +02:00
Calle Pettersson
143705bbf6 Merge pull request #227 from szook/add-worker-process-name-to-w3wp-processes
append IIS worker process name to corresponding w3wp processes
2018-07-23 16:20:07 +02:00
Steve Zook
e8ffb736d0 add worker process name to w3wp processes
modify the `process` collector so that the IIS worker process name is
appended to the corresponding `w3wp` process.

before:
```
wmi_process_private_bytes{creating_process_id="2068",process="w3wp",process_id="12308"} 7.18204928e+08
```

after:
```
wmi_process_private_bytes{creating_process_id="2068",process="w3wp_our.website.com",process_id="12308"} 7.18204928e+08
```

reason:
We have some IIS servers hosting many .NET applications. When there is
resource contention on one of those servers, it's nice to know which IIS
application pool is the culprit.  Having only the process_id to
differentiate between w3wp processes requires additional work to figure
out which is which.  Also it does not allow for historial trending as
the process_id can change across restarts.
2018-07-23 07:53:50 -04:00
Calle Pettersson
21e0f926a3 Merge pull request #226 from szook/fix-iis-collector-panic
fix iis collector panic
2018-07-19 11:27:26 -07:00
Steve Zook
8ea862a3da fix iis collector panic
If the `dst_cache` slice is empty, the program panics when trying to access `dst_cache[0]`

added a `if len(dst_cache) > 0 {}` block around the use of that variable to prevent that panic.
2018-07-19 13:42:00 -04:00
Mark D
bb67658853 Fix IIS warning when IIS collector is NOT in use (#212)
changing IIS code to only error if used
Only tries to read registry if collector is initialized
2018-06-10 07:52:20 -07:00
Calle Pettersson
aecd90dcf1 Merge pull request #209 from martinlindhe/wmi-query-wildcard
* Refactor wmi query generator to use wildcard selector
2018-06-06 03:23:17 -07:00
Calle Pettersson
cd365c6a3b Harmonize query call layout 2018-06-06 11:35:13 +02:00
Calle Pettersson
667d06116d Refactor wmi query generator to use wildcard selector 2018-06-06 10:38:36 +02:00
tunaman
f3072bb4f3 Add extra HyperV VM classes (#202)
add two extra new HyperV VM classes
2018-05-17 23:46:13 -07:00
Calle Pettersson
17072bf257 Merge pull request #198 from martinlindhe/service-status
Add status metric for service-collector
2018-05-17 11:05:07 -07:00
Calle Pettersson
d3d8537201 Merge pull request #200 from martinlindhe/textfile-carriage-return
Strip carriage-returns from textfile input
2018-05-16 05:18:54 -07:00
Calle Pettersson
2951a9ef80 Strip carriage-returns from textfile input 2018-05-15 20:58:58 +02:00
Calle Pettersson
3141fc3ed3 Add status metric for service-collector 2018-05-12 09:49:29 +02:00
Calle Pettersson
a0333ee256 Merge pull request #195 from martinlindhe/totals-as-gauges-help-string
Fix wrong metric types in system collector, improve help-strings
2018-04-30 09:29:00 +02:00
Calle Pettersson
c9fc76de4c Merge pull request #194 from martinlindhe/exporter-start-time
Add process_start_time_seconds for the exporter itself
2018-04-30 09:28:43 +02:00
Calle Pettersson
3752a547d5 Merge pull request #193 from martinlindhe/hyperv-network-labels
Fix missing label on HyperVLegacyNetworkAdapter
2018-04-30 09:28:30 +02:00
Calle Pettersson
0ab6c191be Fix wrong metric types in system collector, improve help-strings 2018-04-29 17:14:53 +02:00
Calle Pettersson
467e83722a Add process_start_time_seconds for the exporter itself 2018-04-29 16:53:34 +02:00
Calle Pettersson
7fe8ca8554 Fix missing label on HyperVLegacyNetworkAdapter 2018-04-29 16:09:18 +02:00
Calle Pettersson
4b3d1d60d9 Add hyperv collector to README 2018-04-18 07:50:05 +02:00
Calle Pettersson
1358123482 Merge pull request #150 from iyacontrol/hyperv
Add hyperv collector
2018-04-18 07:45:05 +02:00
Calle Pettersson
ec79488478 Fix CPU metrics 2018-04-17 20:31:43 +02:00
Calle Pettersson
c3b227a4f2 Switch to common/log 2018-04-17 20:31:14 +02:00
Calle Pettersson
c241513d56 Fix interface->vm labelling of VID class 2018-04-17 18:25:22 +02:00
Calle Pettersson
5a538d7682 Add missing labels 2018-04-17 11:08:36 +02:00
Calle Pettersson
a0ec1e2da6 Rename hyperv subsystem 2018-04-16 20:56:05 +02:00
Calle Pettersson
353de09798 Remove/rename persec metrics 2018-04-16 20:55:47 +02:00
Calle Pettersson
de4838454a Merge pull request #186 from martinlindhe/service-recovery
Set service recovery to restart on crash
2018-04-06 07:19:44 +02:00
Calle Pettersson
86d2b8bdc3 Merge pull request #185 from martinlindhe/create-textfile-directory
Create textfile_inputs directory during install
2018-04-06 07:19:34 +02:00
Calle Pettersson
041ff0351d Merge pull request #183 from martinlindhe/switch-logging
Switch to prometheus/common/log for all logging
2018-04-06 07:19:16 +02:00
Calle Pettersson
076af99418 Set service recovery to restart on crash 2018-04-05 08:37:23 +02:00
Calle Pettersson
76bb06b32f Create textfile_inputs directory during install 2018-04-05 08:17:52 +02:00
Calle Pettersson
be5ac7b440 Switch to prometheus/common/log for all logging 2018-04-05 07:27:26 +02:00
Calle Pettersson
63e51a554b gofmt 2018-04-05 07:11:36 +02:00
Patrick O'Brien
cf792394f3 Bring the textfile collector over from node_exporter (#174)
Bring the textfile collector over from node_exporter

This adds a slightly modified textfile collector from the official
node_exporter project.
2018-04-03 21:37:10 +02:00
Calle Pettersson
5db7c0a936 Merge pull request #112 from martinlindhe/kingpin
Use Kingpin for flags
2018-03-30 09:24:56 +02:00
Calle Pettersson
2461407277 Use kingpin for iis and msmq 2018-03-25 10:01:56 +02:00
Calle Pettersson
33c5e99e0f Add missing vendor 2018-03-25 10:01:56 +02:00
Calle Pettersson
5ecdfe9498 Update README with new flags 2018-03-25 10:01:56 +02:00
Calle Pettersson
caa46799f8 Update installer with new flags 2018-03-25 10:01:56 +02:00
Calle Pettersson
aee1e4b1fd Change to kingpin for flags 2018-03-25 10:01:56 +02:00
Calle Pettersson
69c83b6a39 Fix logrus vendoring (capitalization of import path changed) (#178) 2018-03-25 10:01:15 +02:00
Martin Lindhe
cede267565 installer/build.ps1: autoformat 2018-03-14 11:12:02 +01:00
Martin Lindhe
191debeed6 installer/build.ps1: update to use TLS 1.2 when downloading from github due to https://blog.github.com/2018-02-23-weak-cryptographic-standards-removed/, fixes usage on Win10 2018-03-14 11:11:36 +01:00
Dave Henderson
df0db7a54f Updating deps
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
2018-03-13 21:00:22 +01:00
Martin Lindhe
bda7dd18cf appveyor: re-enable tests 2018-03-13 20:50:31 +01:00
Dave Henderson
617d795383 Use dep instead of govendor
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
2018-03-13 20:48:33 +01:00
Martin Lindhe
b9b8cfd1ca net: export current_bandwidth, fixes #162 2018-02-21 17:06:12 +01:00
Teriand
6b98771187 Add MSMQ collector (#159)
Add MSMQ collector
2018-02-14 12:24:52 +01:00
ghj
afa17b2a1b rename some metrics that maybe misleading 2018-01-24 11:05:42 +08:00
ghj
3b88460eb5 add hyperv 2018-01-23 20:56:00 +08:00
Jorrit Salverda
a52df7696a Add TCP collector (#142)
add tcp collector to get tcp connection usage stats from Win32_PerfRawData_Tcpip_TCPv4
2017-12-20 14:26:30 +01:00
Yuriy Revich
105a1c866b fix: IIS Collector fails, when Application has more than one worker processes (#132)
* fix collecting error with multiple IIS application worker processes
2017-11-13 10:41:35 +01:00
Calle Pettersson
69c1d0faad Merge pull request #128 from gboucher90/master
Add .Net Performance counters
2017-11-07 07:46:47 +01:00
Guillaume Boucher
809fe9becf Add .Net Performance counters 2017-11-06 23:04:13 -05:00
Calle Pettersson
7b6974e595 Merge pull request #120 from martinlindhe/promu-binary-name
Add build.binary to promu config
2017-09-08 11:15:37 +02:00
Calle Pettersson
caf8742dcd Fix promu build 2017-09-08 09:38:33 +01:00
Martin Lindhe
94caf8ee61 correct help message, fixes #118. typo was introduced in a66f0b5475 2017-09-07 14:28:27 +02:00
Calle Pettersson
2f7a372429 Fix "no where-clause" warning in service collector 2017-08-11 17:38:06 +02:00
Calle Pettersson
e547c13716 Merge pull request #107 from martinlindhe/doc-process
README: add an example for -collector.process.processes-where, fixes #105
2017-08-11 12:55:34 +02:00
vbeausoleil
44c39405c7 Added flag to filter services (like the one for processes). Added example for said flag in README.md (#109) 2017-08-11 00:31:39 +02:00
Martin Lindhe
96faedf481 process,iis: fix log call 2017-08-10 03:50:21 +02:00
Martin Lindhe
3cfc11c6d2 ad: fix log call 2017-08-10 03:36:28 +02:00
Martin Lindhe
80f1cf0546 README: update build instructions (#106) 2017-08-10 02:14:06 +02:00
Calle Pettersson
691b672a1e Add DisplayName and Description to Windows service (#100) 2017-08-10 02:13:32 +02:00
Martin Lindhe
7206f020cb README: add an example for -collector.process.processes-where 2017-08-10 02:07:52 +02:00
Martin Lindhe
2e0842573d Makefile: add build command 2017-08-10 01:32:54 +02:00
Calle Pettersson
7537c9896e Add hint in usage about quoting flags in powershell (#95) 2017-07-18 13:18:36 +02:00
Calle Pettersson
88271ddf14 Merge pull request #86 from martinlindhe/iis-wp-and-cache
Implement IIS worker process and server cache classes
2017-07-18 12:52:09 +02:00
Calle Pettersson
f4195aa435 Conditional query for wmi fields added in IIS 8 2017-07-15 13:59:33 +01:00
Calle Pettersson
e8cfeef26c Implement IIS worker process and server cache classes 2017-06-30 20:55:45 +01:00
438 changed files with 88042 additions and 30584 deletions

5
.gitignore vendored
View File

@@ -1,2 +1,5 @@
*.exe
VERSION
VERSION
*.swp
*.un~
output/

View File

@@ -1,6 +1,8 @@
repository:
path: github.com/martinlindhe/wmi_exporter
build:
binaries:
- name: wmi_exporter
ldflags: |
-X {{repoPath}}/vendor/github.com/prometheus/common/version.Version={{.Version}}
-X {{repoPath}}/vendor/github.com/prometheus/common/version.Revision={{.Revision}}
@@ -10,3 +12,7 @@ build:
tarball:
files:
- LICENSE
crossbuild:
platforms:
- windows/amd64
- windows/386

175
Gopkg.lock generated Normal file
View File

@@ -0,0 +1,175 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:3ccf8ba7afe02fd470c4f07d6eea4d0e6875da3d129f95b925f2003ce5dd2024"
name = "github.com/StackExchange/wmi"
packages = ["."]
pruneopts = "NUT"
revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338"
version = "1.0.0"
[[projects]]
branch = "master"
digest = "1:f3793f8a708522400cef1dba23385e901aede5519f68971fd69938ef330b07a1"
name = "github.com/alecthomas/template"
packages = [
".",
"parse",
]
pruneopts = "NUT"
revision = "a0175ee3bccc567396460bf5acd36800cb10c49c"
[[projects]]
branch = "master"
digest = "1:fdd419e104ec26bb5bd63cc62637c640453ed2929a7453f3afadbd9a0223da66"
name = "github.com/alecthomas/units"
packages = ["."]
pruneopts = "NUT"
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
[[projects]]
branch = "master"
digest = "1:cb0535f5823b47df7dcb9768ebb6c000b79ad115472910c70efe93c9ed9b2315"
name = "github.com/beorn7/perks"
packages = ["quantile"]
pruneopts = "NUT"
revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9"
[[projects]]
digest = "1:f9adc21a937e5da643ea14a3488cb7506788876737a5e205394e508627a6eec8"
name = "github.com/dimchansky/utfbom"
packages = ["."]
pruneopts = "NUT"
revision = "d2133a1ce379ef6fa992b0514a77146c60db9d1c"
version = "v1.1.0"
[[projects]]
digest = "1:cb4e216bd9f58866f42dc65893455b24f879b026fdaa1ecc3aafff625fdb5a66"
name = "github.com/go-ole/go-ole"
packages = [
".",
"oleutil",
]
pruneopts = "NUT"
revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506"
version = "v1.2.1"
[[projects]]
digest = "1:9f35c1344b56e5868d511d231f215edd0650aa572664f856444affdd256e43e4"
name = "github.com/golang/protobuf"
packages = ["proto"]
pruneopts = "NUT"
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
version = "v1.0.0"
[[projects]]
digest = "1:5985ef4caf91ece5d54817c11ea25f182697534f8ae6521eadcd628c142ac4b6"
name = "github.com/matttproud/golang_protobuf_extensions"
packages = ["pbutil"]
pruneopts = "NUT"
revision = "3247c84500bff8d9fb6d579d800f20b3e091582c"
version = "v1.0.0"
[[projects]]
digest = "1:03bca087b180bf24c4f9060775f137775550a0834e18f0bca0520a868679dbd7"
name = "github.com/prometheus/client_golang"
packages = [
"prometheus",
"prometheus/promhttp",
]
pruneopts = "NUT"
revision = "c5b7fccd204277076155f10851dad72b76a49317"
version = "v0.8.0"
[[projects]]
branch = "master"
digest = "1:32d10bdfa8f09ecf13598324dba86ab891f11db3c538b6a34d1c3b5b99d7c36b"
name = "github.com/prometheus/client_model"
packages = ["go"]
pruneopts = "NUT"
revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c"
[[projects]]
branch = "master"
digest = "1:ce98e83b2b9486b6a9ce5e44fd4097c64e8f2f0eaa6c5041a8f12d3aaa5c17b3"
name = "github.com/prometheus/common"
packages = [
"expfmt",
"internal/bitbucket.org/ww/goautoneg",
"log",
"model",
"version",
]
pruneopts = "NUT"
revision = "e4aa40a9169a88835b849a6efb71e05dc04b88f0"
[[projects]]
branch = "master"
digest = "1:61a95e8d3e39e94207fba1b56d3c2182a356a1e41017aa647f523ae964b6bb0c"
name = "github.com/prometheus/procfs"
packages = [
".",
"internal/util",
"nfs",
"xfs",
]
pruneopts = "NUT"
revision = "54d17b57dd7d4a3aa092476596b3f8a933bde349"
[[projects]]
digest = "1:6989062eb7ccf25cf38bf4fe3dba097ee209f896cda42cefdca3927047bef7b6"
name = "github.com/sirupsen/logrus"
packages = ["."]
pruneopts = "NUT"
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
version = "v1.0.5"
[[projects]]
branch = "master"
digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
pruneopts = "NUT"
revision = "182114d582623c1caa54f73de9c7224e23a48487"
[[projects]]
branch = "master"
digest = "1:ea69008276e11262595a1f9a279ffd51d93e21c32c13b0f81856e962c6f607dd"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
"windows/registry",
"windows/svc",
"windows/svc/eventlog",
]
pruneopts = "NUT"
revision = "8c0ece68c28377f4c326d85b94f8df0dace46f80"
[[projects]]
digest = "1:22b2dee6f30bc8601f087449a2a819df8388e54e9547349c658f14d8f8c590f2"
name = "gopkg.in/alecthomas/kingpin.v2"
packages = ["."]
pruneopts = "NUT"
revision = "947dcec5ba9c011838740e680966fd7087a71d0d"
version = "v2.2.6"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/StackExchange/wmi",
"github.com/dimchansky/utfbom",
"github.com/prometheus/client_golang/prometheus",
"github.com/prometheus/client_golang/prometheus/promhttp",
"github.com/prometheus/client_model/go",
"github.com/prometheus/common/expfmt",
"github.com/prometheus/common/log",
"github.com/prometheus/common/version",
"golang.org/x/sys/windows/registry",
"golang.org/x/sys/windows/svc",
"gopkg.in/alecthomas/kingpin.v2",
]
solver-name = "gps-cdcl"
solver-version = 1

4
Gopkg.toml Normal file
View File

@@ -0,0 +1,4 @@
[prune]
non-go = true
go-tests = true
unused-packages = true

View File

@@ -1,2 +1,19 @@
export GOOS=windows
build:
promu build -v
test:
go test -v ./...
lint:
gometalinter --vendor --config gometalinter.config ./...
fmt:
gofmt -l -w -s .
crossbuild:
# The prometheus/golang-builder image for promu crossbuild doesn't exist
# on Windows, so for now, we'll just build twice
GOARCH=amd64 promu build --prefix=output/amd64
GOARCH=386 promu build --prefix=output/386

View File

@@ -9,20 +9,34 @@ Prometheus exporter for Windows machines, using the WMI (Windows Management Inst
Name | Description | Enabled by default
---------|-------------|--------------------
ad | [Win32_PerfRawData_DirectoryServices_DirectoryServices](https://msdn.microsoft.com/en-us/library/ms803980.aspx) Active Directory |
cpu | [Win32_PerfRawData_PerfOS_Processor](https://msdn.microsoft.com/en-us/library/aa394317(v=vs.90).aspx) metrics (cpu usage) | &#10003;
cs | [Win32_ComputerSystem](https://msdn.microsoft.com/en-us/library/aa394102) metrics (system properties, num cpus/total memory) | &#10003;
dns | [Win32_PerfRawData_DNS_DNS](https://technet.microsoft.com/en-us/library/cc977686.aspx) metrics (DNS Server) |
iis | [Win32_PerfRawData_W3SVC_WebService](https://msdn.microsoft.com/en-us/library/aa394345) IIS metrics |
logical_disk | [Win32_PerfRawData_PerfDisk_LogicalDisk](https://msdn.microsoft.com/en-us/windows/hardware/aa394307(v=vs.71)) metrics (disk I/O) | &#10003;
net | [Win32_PerfRawData_Tcpip_NetworkInterface](https://technet.microsoft.com/en-us/security/aa394340(v=vs.80)) metrics (network interface I/O) | &#10003;
os | [Win32_OperatingSystem](https://msdn.microsoft.com/en-us/library/aa394239) metrics (memory, processes, users) | &#10003;
process | [Win32_PerfRawData_PerfProc_Process](https://msdn.microsoft.com/en-us/library/aa394323(v=vs.85).aspx) metrics (per-process stats) |
service | [Win32_Service](https://msdn.microsoft.com/en-us/library/aa394418(v=vs.85).aspx) metrics (service states) | &#10003;
system | Win32_PerfRawData_PerfOS_System metrics (system calls) | &#10003;
vmware | Performance counters installed by the Vmware Guest agent |
[ad](docs/collector.ad.md) | Active Directory Domain Services |
[cpu](docs/collector.cpu.md) | CPU usage | &#10003;
[cs](docs/collector.cs.md) | "Computer System" metrics (system properties, num cpus/total memory) | &#10003;
[dns](docs/collector.dns.md) | DNS Server |
[hyperv](docs/collector.hyperv.md) | Hyper-V hosts |
[iis](docs/collector.iis.md) | IIS sites and applications |
[logical_disk](docs/collector.logical_disk.md) | Logical disks, disk I/O | &#10003;
[memory](docs/collector.memory.md) | Memory usage metrics |
[msmq](docs/collector.msmq.md) | MSMQ queues |
[mssql](docs/collector.mssql.md) | [SQL Server Performance Objects](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/use-sql-server-objects#SQLServerPOs) metrics |
[netframework_clrexceptions](docs/collector.netframework_clrexceptions.md) | .NET Framework CLR Exceptions |
[netframework_clrinterop](docs/collector.netframework_clrinterop.md) | .NET Framework Interop Metrics |
[netframework_clrjit](docs/collector.netframework_clrjit.md) | .NET Framework JIT metrics |
[netframework_clrloading](docs/collector.netframework_clrloading.md) | .NET Framework CLR Loading metrics |
[netframework_clrlocksandthreads](docs/collector.netframework_clrlocksandthreads.md) | .NET Framework locks and metrics threads |
[netframework_clrmemory](docs/collector.netframework_clrmemory.md) | .NET Framework Memory metrics |
[netframework_clrremoting](docs/collector.netframework_clrremoting.md) | .NET Framework Remoting metrics |
[netframework_clrsecurity](docs/collector.netframework_clrsecurity.md) | .NET Framework Security Check metrics |
[net](docs/collector.net.md) | Network interface I/O | &#10003;
[os](docs/collector.os.md) | OS metrics (memory, processes, users) | &#10003;
[process](docs/collector.process.md) | Per-process metrics |
[service](docs/collector.service.md) | Service state metrics | &#10003;
[system](docs/collector.system.md) | System calls | &#10003;
[tcp](docs/collector.tcp.md) | TCP connections |
[textfile](docs/collector.textfile.md) | Read prometheus metrics from a text file | &#10003;
[vmware](docs/collector.vmware.md) | Performance counters installed by the Vmware Guest agent |
The HELP texts shows the WMI data source, please see MSDN documentation for details.
See the linked documentation on each collector for more information on reported metrics, configuration settings and usage examples.
## Installation
The latest release can be downloaded from the [releases page](https://github.com/martinlindhe/wmi_exporter/releases).
@@ -33,17 +47,24 @@ If the installer is run without any parameters, the exporter will run with defau
Name | Description
-----|------------
`ENABLED_COLLECTORS` | As the `-collectors.enabled` flag, provide a comma-separated list of enabled collectors
`ENABLED_COLLECTORS` | As the `--collectors.enabled` flag, provide a comma-separated list of enabled collectors
`LISTEN_ADDR` | The IP address to bind to. Defaults to 0.0.0.0
`LISTEN_PORT` | The port to bind to. Defaults to 9182.
`METRICS_PATH` | The path at which to serve metrics. Defaults to `/metrics`
`TEXTFILE_DIR` | As the `--collector.textfile.directory` flag, provide a directory to read text files with metrics from
`EXTRA_FLAGS` | Allows passing full CLI flags. Defaults to an empty string.
Parameters are sent to the installer via `msiexec`. Example invocation:
Parameters are sent to the installer via `msiexec`. Example invocations:
```powershell
msiexec /i <path-to-msi-file> ENABLED_COLLECTORS=os,iis LISTEN_PORT=5000
```
Example service collector with a custom query.
```powershell
msiexec /i <path-to-msi-file> ENABLED_COLLECTORS=os,service --% EXTRA_FLAGS="--collector.service.services-where ""Name LIKE 'sql%'"""
```
## Roadmap
See [open issues](https://github.com/martinlindhe/wmi_exporter/issues)
@@ -51,14 +72,29 @@ See [open issues](https://github.com/martinlindhe/wmi_exporter/issues)
## Usage
go get -u github.com/kardianos/govendor
go get -u github.com/golang/dep
go get -u github.com/prometheus/promu
go get -u github.com/martinlindhe/wmi_exporter
cd $env:GOPATH/src/github.com/martinlindhe/wmi_exporter
govendor build +local
promu build -v .
.\wmi_exporter.exe
The prometheus metrics will be exposed on [localhost:9182](http://localhost:9182)
## Examples
### Enable only service collector and specify a custom query
.\wmi_exporter.exe --collectors.enabled "service" --collector.service.services-where "Name='wmi_exporter'"
### Enable only process collector and specify a custom query
.\wmi_exporter.exe --collectors.enabled "process" --collector.process.processes-where "Name LIKE 'firefox%'"
When there are multiple processes with the same name, WMI represents those after the first instance as `process-name#index`. So to get them all, rather than just the first one, the query needs to be a wildcard search using a `%` character.
Please note that in Windows batch scripts (and when using the `cmd` command prompt), the `%` character is reserved, so it has to be escaped with another `%`. For example, the wildcard syntax for searching for all firefox processes is `firefox%%`.
## License

View File

@@ -1,52 +1,64 @@
version: "{build}"
os: Windows Server 2012 R2
environment:
GOPATH: c:\gopath
matrix:
- MSI_ARCH: amd64
GOARCH: amd64
- MSI_ARCH: 386
GOARCH: 386
clone_folder: c:\gopath\src\github.com\martinlindhe\wmi_exporter
install:
- go version
- set PATH=%GOPATH%\bin;c:\go\bin;%GOPATH%\bin\windows_%GOARCH%;%PATH%
- go get -u github.com/kardianos/govendor
- go get -u github.com/prometheus/promu
- choco install gitversion.portable -y
build_script:
- ps: gitversion /output json /showvariable FullSemVer | Set-Content VERSION -PassThru
- govendor test -v +local
- promu build -v .
- ps: |
$ErrorActionPreference = "Stop"
if($env:APPVEYOR_REPO_TAG -eq "True") {
# The MSI version is not semver compliant, so just take the numerical parts
$Version = $env:APPVEYOR_REPO_TAG_NAME -replace '^v?([0-9\.]+).*$','$1'
Write-Verbose "Setting msi version to $Version"
.\installer\build.ps1 -PathToExecutable .\wmi_exporter.exe -Version $Version -Arch "$env:MSI_ARCH"
Push-AppveyorArtifact installer\Output\wmi_exporter-$Version-$env:MSI_ARCH.msi -DeploymentName Installer
}
after_build:
- 7z a wmi_exporter-%MSI_ARCH%.zip wmi_exporter.exe
artifacts:
- name: Executable
path: 'wmi_exporter-*.zip'
deploy:
- provider: GitHub
description: WMI Exporter version $(appveyor_build_version)
artifact: Executable,Installer
auth_token:
secure: 'CrXWeTf7qONUOEki5olFfGEUPMLDeHj61koDXV3OVEaLgtACmnVHsKUub9POflda'
draft: false
prerelease: false
on:
appveyor_repo_tag: true
version: "{build}"
os: Visual Studio 2017
build: off
stack: go 1.10
environment:
GOPATH: c:\gopath
clone_folder: c:\gopath\src\github.com\martinlindhe\wmi_exporter
install:
- set PATH=%GOPATH%\bin;%PATH%
- go get -u github.com/prometheus/promu
- go get -u github.com/alecthomas/gometalinter && gometalinter --install
- choco install gitversion.portable make -y
test_script:
- make test
after_test:
- make lint
build_script:
- ps: |
$ErrorActionPreference = "Stop"
gitversion /output json /showvariable FullSemVer | Set-Content VERSION -PassThru
$Version = Get-Content VERSION
make crossbuild
# GH requires all files to have different names, so add version/arch to differentiate
foreach($Arch in "amd64","386") {
Rename-Item output\$Arch\wmi_exporter.exe -NewName wmi_exporter-$Version-$Arch.exe
}
after_build:
- ps: |
# Build installer packages only on tagged releases
if($env:APPVEYOR_REPO_TAG -ne "True") {
return
}
$ErrorActionPreference = "Stop"
# The MSI version is not semver compliant, so just take the numerical parts
$Version = $env:APPVEYOR_REPO_TAG_NAME -replace '^v?([0-9\.]+).*$','$1'
foreach($Arch in "amd64","386") {
Write-Verbose "Building wmi_exporter $Version msi for $Arch"
.\installer\build.ps1 -PathToExecutable .\output\$Arch\wmi_exporter-$Version-$Arch.exe -Version $Version -Arch "$Arch"
Move-Item installer\Output\wmi_exporter-$Version-$Arch.msi output\$Arch\
}
- promu checksum output\
artifacts:
- name: Artifacts
path: output\**\*
deploy:
- provider: GitHub
description: WMI Exporter version $(appveyor_build_version)
artifact: Artifacts
auth_token:
secure: 'CrXWeTf7qONUOEki5olFfGEUPMLDeHj61koDXV3OVEaLgtACmnVHsKUub9POflda'
draft: false
prerelease: false
on:
appveyor_repo_tag: true

View File

@@ -1,8 +1,10 @@
// returns data points from Win32_PerfRawData_DirectoryServices_DirectoryServices
// Partial docs: https://msdn.microsoft.com/en-us/library/ms803980.aspx
// +build windows
package collector
import (
"errors"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
@@ -454,12 +456,14 @@ func NewADCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *ADCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Errorf("[ERROR] failed collecting ad metrics:", desc, err)
log.Error("failed collecting ad metrics:", desc, err)
return err
}
return nil
}
// Win32_PerfRawData_DirectoryServices_DirectoryServices docs:
// - https://msdn.microsoft.com/en-us/library/ms803980.aspx
type Win32_PerfRawData_DirectoryServices_DirectoryServices struct {
Name string
@@ -483,8 +487,8 @@ type Win32_PerfRawData_DirectoryServices_DirectoryServices struct {
DatabasemodifysPersec uint32
DatabaserecyclesPersec uint32
DigestBindsPersec uint32
DRAHighestUSNCommittedHighpart uint32
DRAHighestUSNCommittedLowpart uint32
DRAHighestUSNCommittedHighpart uint64
DRAHighestUSNCommittedLowpart uint64
DRAHighestUSNIssuedHighpart uint64
DRAHighestUSNIssuedLowpart uint64
DRAInboundBytesCompressedBetweenSitesAfterCompressionPersec uint32
@@ -612,10 +616,13 @@ type Win32_PerfRawData_DirectoryServices_DirectoryServices struct {
func (c *ADCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_DirectoryServices_DirectoryServices
q := wmi.CreateQuery(&dst, "")
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
if len(dst) == 0 {
return nil, errors.New("WMI query returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.AddressBookOperationsTotal,

View File

@@ -1,13 +1,13 @@
// returns data points from Win32_PerfRawData_PerfOS_Processor
// https://msdn.microsoft.com/en-us/library/aa394317(v=vs.90).aspx - Win32_PerfRawData_PerfOS_Processor class
// +build windows
package collector
import (
"log"
"strings"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
@@ -22,6 +22,7 @@ type CPUCollector struct {
DPCsTotal *prometheus.Desc
}
// NewCPUCollector constructs a new CPUCollector
func NewCPUCollector() (Collector, error) {
const subsystem = "cpu"
return &CPUCollector{
@@ -57,12 +58,14 @@ func NewCPUCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *CPUCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Println("[ERROR] failed collecting cpu metrics:", desc, err)
log.Error("failed collecting cpu metrics:", desc, err)
return err
}
return nil
}
// Win32_PerfRawData_PerfOS_Processor docs:
// - https://msdn.microsoft.com/en-us/library/aa394317(v=vs.90).aspx
type Win32_PerfRawData_PerfOS_Processor struct {
Name string
C1TransitionsPersec uint64
@@ -117,7 +120,7 @@ type Win32_PerfRawData_Counters_ProcessorInformation struct {
func (c *CPUCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_PerfOS_Processor
q := wmi.CreateQuery(&dst, "")
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}

View File

@@ -1,13 +1,13 @@
// returns data points from Win32_ComputerSystem
// https://msdn.microsoft.com/en-us/library/aa394102 - Win32_ComputerSystem class
// +build windows
package collector
import (
"log"
"errors"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
@@ -44,12 +44,14 @@ func NewCSCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *CSCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Println("[ERROR] failed collecting cs metrics:", desc, err)
log.Error("failed collecting cs metrics:", desc, err)
return err
}
return nil
}
// Win32_ComputerSystem docs:
// - https://msdn.microsoft.com/en-us/library/aa394102
type Win32_ComputerSystem struct {
NumberOfLogicalProcessors uint32
TotalPhysicalMemory uint64
@@ -57,9 +59,13 @@ type Win32_ComputerSystem struct {
func (c *CSCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_ComputerSystem
if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil {
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
if len(dst) == 0 {
return nil, errors.New("WMI query returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.LogicalProcessors,

View File

@@ -1,13 +1,13 @@
// returns data points from Win32_PerfRawData_DNS_DNS
// https://msdn.microsoft.com/en-us/library/ms803992.aspx?f=255&MSPPError=-2147217396
// https://technet.microsoft.com/en-us/library/cc977686.aspx
// +build windows
package collector
import (
"log"
"errors"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
@@ -183,12 +183,15 @@ func NewDNSCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *DNSCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Println("[ERROR] failed collecting dns metrics:", desc, err)
log.Error("failed collecting dns metrics:", desc, err)
return err
}
return nil
}
// Win32_PerfRawData_DNS_DNS docs:
// - https://msdn.microsoft.com/en-us/library/ms803992.aspx?f=255&MSPPError=-2147217396
// - https://technet.microsoft.com/en-us/library/cc977686.aspx
type Win32_PerfRawData_DNS_DNS struct {
AXFRRequestReceived uint32
AXFRRequestSent uint32
@@ -234,10 +237,13 @@ type Win32_PerfRawData_DNS_DNS struct {
func (c *DNSCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_DNS_DNS
q := wmi.CreateQuery(&dst, "")
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
if len(dst) == 0 {
return nil, errors.New("WMI query returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.ZoneTransferRequestsReceived,

1423
collector/hyperv.go Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,15 @@
// returns data points from Win32_PerfRawData_PerfDisk_LogicalDisk
// https://msdn.microsoft.com/en-us/windows/hardware/aa394307(v=vs.71) - Win32_PerfRawData_PerfDisk_LogicalDisk class
// https://msdn.microsoft.com/en-us/library/ms803973.aspx - LogicalDisk object reference
// +build windows
package collector
import (
"flag"
"fmt"
"log"
"regexp"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"gopkg.in/alecthomas/kingpin.v2"
)
func init() {
@@ -19,8 +17,14 @@ func init() {
}
var (
volumeWhitelist = flag.String("collector.logical_disk.volume-whitelist", ".+", "Regexp of volumes to whitelist. Volume name must both match whitelist and not match blacklist to be included.")
volumeBlacklist = flag.String("collector.logical_disk.volume-blacklist", "", "Regexp of volumes to blacklist. Volume name must both match whitelist and not match blacklist to be included.")
volumeWhitelist = kingpin.Flag(
"collector.logical_disk.volume-whitelist",
"Regexp of volumes to whitelist. Volume name must both match whitelist and not match blacklist to be included.",
).Default(".+").String()
volumeBlacklist = kingpin.Flag(
"collector.logical_disk.volume-blacklist",
"Regexp of volumes to blacklist. Volume name must both match whitelist and not match blacklist to be included.",
).Default("").String()
)
// A LogicalDiskCollector is a Prometheus collector for WMI Win32_PerfRawData_PerfDisk_LogicalDisk metrics
@@ -132,12 +136,15 @@ func NewLogicalDiskCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *LogicalDiskCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Println("[ERROR] failed collecting logical_disk metrics:", desc, err)
log.Error("failed collecting logical_disk metrics:", desc, err)
return err
}
return nil
}
// Win32_PerfRawData_PerfDisk_LogicalDisk docs:
// - https://msdn.microsoft.com/en-us/windows/hardware/aa394307(v=vs.71) - Win32_PerfRawData_PerfDisk_LogicalDisk class
// - https://msdn.microsoft.com/en-us/library/ms803973.aspx - LogicalDisk object reference
type Win32_PerfRawData_PerfDisk_LogicalDisk struct {
Name string
CurrentDiskQueueLength uint32
@@ -155,7 +162,7 @@ type Win32_PerfRawData_PerfDisk_LogicalDisk struct {
func (c *LogicalDiskCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_PerfDisk_LogicalDisk
q := wmi.CreateQuery(&dst, "")
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}

504
collector/memory.go Normal file
View File

@@ -0,0 +1,504 @@
// returns data points from Win32_PerfRawData_PerfOS_Memory
// <add link to documentation here> - Win32_PerfRawData_PerfOS_Memory class
// +build windows
package collector
import (
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["memory"] = NewMemoryCollector
}
// A MemoryCollector is a Prometheus collector for WMI Win32_PerfRawData_PerfOS_Memory metrics
type MemoryCollector struct {
AvailableBytes *prometheus.Desc
CacheBytes *prometheus.Desc
CacheBytesPeak *prometheus.Desc
CacheFaultsTotal *prometheus.Desc
CommitLimit *prometheus.Desc
CommittedBytes *prometheus.Desc
DemandZeroFaultsTotal *prometheus.Desc
FreeAndZeroPageListBytes *prometheus.Desc
FreeSystemPageTableEntries *prometheus.Desc
ModifiedPageListBytes *prometheus.Desc
PageFaultsTotal *prometheus.Desc
SwapPageReadsTotal *prometheus.Desc
SwapPagesReadTotal *prometheus.Desc
SwapPagesWrittenTotal *prometheus.Desc
SwapPageOperationsTotal *prometheus.Desc
SwapPageWritesTotal *prometheus.Desc
PoolNonpagedAllocsTotal *prometheus.Desc
PoolNonpagedBytes *prometheus.Desc
PoolPagedAllocsTotal *prometheus.Desc
PoolPagedBytes *prometheus.Desc
PoolPagedResidentBytes *prometheus.Desc
StandbyCacheCoreBytes *prometheus.Desc
StandbyCacheNormalPriorityBytes *prometheus.Desc
StandbyCacheReserveBytes *prometheus.Desc
SystemCacheResidentBytes *prometheus.Desc
SystemCodeResidentBytes *prometheus.Desc
SystemCodeTotalBytes *prometheus.Desc
SystemDriverResidentBytes *prometheus.Desc
SystemDriverTotalBytes *prometheus.Desc
TransitionFaultsTotal *prometheus.Desc
TransitionPagesRepurposedTotal *prometheus.Desc
WriteCopiesTotal *prometheus.Desc
}
// NewMemoryCollector ...
func NewMemoryCollector() (Collector, error) {
const subsystem = "memory"
return &MemoryCollector{
AvailableBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "available_bytes"),
"The amount of physical memory immediately available for allocation to a process or for system use. It is equal to the sum of memory assigned to"+
" the standby (cached), free and zero page lists (AvailableBytes)",
nil,
nil,
),
CacheBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "cache_bytes"),
"(CacheBytes)",
nil,
nil,
),
CacheBytesPeak: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "cache_bytes_peak"),
"(CacheBytesPeak)",
nil,
nil,
),
CacheFaultsTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "cache_faults_total"),
"(CacheFaultsPersec)",
nil,
nil,
),
CommitLimit: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "commit_limit"),
"(CommitLimit)",
nil,
nil,
),
CommittedBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "committed_bytes"),
"(CommittedBytes)",
nil,
nil,
),
DemandZeroFaultsTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "demand_zero_faults_total"),
"The number of zeroed pages required to satisfy faults. Zeroed pages, pages emptied of previously stored data and filled with zeros, are a security"+
" feature of Windows that prevent processes from seeing data stored by earlier processes that used the memory space (DemandZeroFaults)",
nil,
nil,
),
FreeAndZeroPageListBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "free_and_zero_page_list_bytes"),
"(FreeAndZeroPageListBytes)",
nil,
nil,
),
FreeSystemPageTableEntries: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "free_system_page_table_entries"),
"(FreeSystemPageTableEntries)",
nil,
nil,
),
ModifiedPageListBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "modified_page_list_bytes"),
"(ModifiedPageListBytes)",
nil,
nil,
),
PageFaultsTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "page_faults_total"),
"(PageFaultsPersec)",
nil,
nil,
),
SwapPageReadsTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "swap_page_reads_total"),
"Number of disk page reads (a single read operation reading several pages is still only counted once) (PageReadsPersec)",
nil,
nil,
),
SwapPagesReadTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "swap_pages_read_total"),
"Number of pages read across all page reads (ie counting all pages read even if they are read in a single operation) (PagesInputPersec)",
nil,
nil,
),
SwapPagesWrittenTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "swap_pages_written_total"),
"Number of pages written across all page writes (ie counting all pages written even if they are written in a single operation) (PagesOutputPersec)",
nil,
nil,
),
SwapPageOperationsTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "swap_page_operations_total"),
"Total number of swap page read and writes (PagesPersec)",
nil,
nil,
),
SwapPageWritesTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "swap_page_writes_total"),
"Number of disk page writes (a single write operation writing several pages is still only counted once) (PageWritesPersec)",
nil,
nil,
),
PoolNonpagedAllocsTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "pool_nonpaged_allocs_total"),
"The number of calls to allocate space in the nonpaged pool. The nonpaged pool is an area of system memory area for objects that cannot be written"+
" to disk, and must remain in physical memory as long as they are allocated (PoolNonpagedAllocs)",
nil,
nil,
),
PoolNonpagedBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "pool_nonpaged_bytes_total"),
"(PoolNonpagedBytes)",
nil,
nil,
),
PoolPagedAllocsTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "pool_paged_allocs_total"),
"(PoolPagedAllocs)",
nil,
nil,
),
PoolPagedBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "pool_paged_bytes"),
"(PoolPagedBytes)",
nil,
nil,
),
PoolPagedResidentBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "pool_paged_resident_bytes"),
"(PoolPagedResidentBytes)",
nil,
nil,
),
StandbyCacheCoreBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "standby_cache_core_bytes"),
"(StandbyCacheCoreBytes)",
nil,
nil,
),
StandbyCacheNormalPriorityBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "standby_cache_normal_priority_bytes"),
"(StandbyCacheNormalPriorityBytes)",
nil,
nil,
),
StandbyCacheReserveBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "standby_cache_reserve_bytes"),
"(StandbyCacheReserveBytes)",
nil,
nil,
),
SystemCacheResidentBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "system_cache_resident_bytes"),
"(SystemCacheResidentBytes)",
nil,
nil,
),
SystemCodeResidentBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "system_code_resident_bytes"),
"(SystemCodeResidentBytes)",
nil,
nil,
),
SystemCodeTotalBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "system_code_total_bytes"),
"(SystemCodeTotalBytes)",
nil,
nil,
),
SystemDriverResidentBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "system_driver_resident_bytes"),
"(SystemDriverResidentBytes)",
nil,
nil,
),
SystemDriverTotalBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "system_driver_total_bytes"),
"(SystemDriverTotalBytes)",
nil,
nil,
),
TransitionFaultsTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "transition_faults_total"),
"(TransitionFaultsPersec)",
nil,
nil,
),
TransitionPagesRepurposedTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "transition_pages_repurposed_total"),
"(TransitionPagesRePurposedPersec)",
nil,
nil,
),
WriteCopiesTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "write_copies_total"),
"The number of page faults caused by attempting to write that were satisfied by copying the page from elsewhere in physical memory (WriteCopiesPersec)",
nil,
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *MemoryCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting memory metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_PerfOS_Memory struct {
AvailableBytes uint64
AvailableKBytes uint64
AvailableMBytes uint64
CacheBytes uint64
CacheBytesPeak uint64
CacheFaultsPersec uint32
CommitLimit uint64
CommittedBytes uint64
DemandZeroFaultsPersec uint32
FreeAndZeroPageListBytes uint64
FreeSystemPageTableEntries uint32
ModifiedPageListBytes uint64
PageFaultsPersec uint32
PageReadsPersec uint32
PagesInputPersec uint32
PagesOutputPersec uint32
PagesPersec uint32
PageWritesPersec uint32
PoolNonpagedAllocs uint32
PoolNonpagedBytes uint64
PoolPagedAllocs uint32
PoolPagedBytes uint64
PoolPagedResidentBytes uint64
StandbyCacheCoreBytes uint64
StandbyCacheNormalPriorityBytes uint64
StandbyCacheReserveBytes uint64
SystemCacheResidentBytes uint64
SystemCodeResidentBytes uint64
SystemCodeTotalBytes uint64
SystemDriverResidentBytes uint64
SystemDriverTotalBytes uint64
TransitionFaultsPersec uint32
TransitionPagesRePurposedPersec uint32
WriteCopiesPersec uint32
}
func (c *MemoryCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_PerfOS_Memory
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
ch <- prometheus.MustNewConstMetric(
c.AvailableBytes,
prometheus.GaugeValue,
float64(dst[0].AvailableBytes),
)
ch <- prometheus.MustNewConstMetric(
c.CacheBytes,
prometheus.GaugeValue,
float64(dst[0].CacheBytes),
)
ch <- prometheus.MustNewConstMetric(
c.CacheBytesPeak,
prometheus.GaugeValue,
float64(dst[0].CacheBytesPeak),
)
ch <- prometheus.MustNewConstMetric(
c.CacheFaultsTotal,
prometheus.GaugeValue,
float64(dst[0].CacheFaultsPersec),
)
ch <- prometheus.MustNewConstMetric(
c.CommitLimit,
prometheus.GaugeValue,
float64(dst[0].CommitLimit),
)
ch <- prometheus.MustNewConstMetric(
c.CommittedBytes,
prometheus.GaugeValue,
float64(dst[0].CommittedBytes),
)
ch <- prometheus.MustNewConstMetric(
c.DemandZeroFaultsTotal,
prometheus.GaugeValue,
float64(dst[0].DemandZeroFaultsPersec),
)
ch <- prometheus.MustNewConstMetric(
c.FreeAndZeroPageListBytes,
prometheus.GaugeValue,
float64(dst[0].FreeAndZeroPageListBytes),
)
ch <- prometheus.MustNewConstMetric(
c.FreeSystemPageTableEntries,
prometheus.GaugeValue,
float64(dst[0].FreeSystemPageTableEntries),
)
ch <- prometheus.MustNewConstMetric(
c.ModifiedPageListBytes,
prometheus.GaugeValue,
float64(dst[0].ModifiedPageListBytes),
)
ch <- prometheus.MustNewConstMetric(
c.PageFaultsTotal,
prometheus.GaugeValue,
float64(dst[0].PageFaultsPersec),
)
ch <- prometheus.MustNewConstMetric(
c.SwapPageReadsTotal,
prometheus.GaugeValue,
float64(dst[0].PageReadsPersec),
)
ch <- prometheus.MustNewConstMetric(
c.SwapPagesReadTotal,
prometheus.GaugeValue,
float64(dst[0].PagesInputPersec),
)
ch <- prometheus.MustNewConstMetric(
c.SwapPagesWrittenTotal,
prometheus.GaugeValue,
float64(dst[0].PagesOutputPersec),
)
ch <- prometheus.MustNewConstMetric(
c.SwapPageOperationsTotal,
prometheus.GaugeValue,
float64(dst[0].PagesPersec),
)
ch <- prometheus.MustNewConstMetric(
c.SwapPageWritesTotal,
prometheus.GaugeValue,
float64(dst[0].PageWritesPersec),
)
ch <- prometheus.MustNewConstMetric(
c.PoolNonpagedAllocsTotal,
prometheus.GaugeValue,
float64(dst[0].PoolNonpagedAllocs),
)
ch <- prometheus.MustNewConstMetric(
c.PoolNonpagedBytes,
prometheus.GaugeValue,
float64(dst[0].PoolNonpagedBytes),
)
ch <- prometheus.MustNewConstMetric(
c.PoolPagedAllocsTotal,
prometheus.GaugeValue,
float64(dst[0].PoolPagedAllocs),
)
ch <- prometheus.MustNewConstMetric(
c.PoolPagedBytes,
prometheus.GaugeValue,
float64(dst[0].PoolPagedBytes),
)
ch <- prometheus.MustNewConstMetric(
c.PoolPagedResidentBytes,
prometheus.GaugeValue,
float64(dst[0].PoolPagedResidentBytes),
)
ch <- prometheus.MustNewConstMetric(
c.StandbyCacheCoreBytes,
prometheus.GaugeValue,
float64(dst[0].StandbyCacheCoreBytes),
)
ch <- prometheus.MustNewConstMetric(
c.StandbyCacheNormalPriorityBytes,
prometheus.GaugeValue,
float64(dst[0].StandbyCacheNormalPriorityBytes),
)
ch <- prometheus.MustNewConstMetric(
c.StandbyCacheReserveBytes,
prometheus.GaugeValue,
float64(dst[0].StandbyCacheReserveBytes),
)
ch <- prometheus.MustNewConstMetric(
c.SystemCacheResidentBytes,
prometheus.GaugeValue,
float64(dst[0].SystemCacheResidentBytes),
)
ch <- prometheus.MustNewConstMetric(
c.SystemCodeResidentBytes,
prometheus.GaugeValue,
float64(dst[0].SystemCodeResidentBytes),
)
ch <- prometheus.MustNewConstMetric(
c.SystemCodeTotalBytes,
prometheus.GaugeValue,
float64(dst[0].SystemCodeTotalBytes),
)
ch <- prometheus.MustNewConstMetric(
c.SystemDriverResidentBytes,
prometheus.GaugeValue,
float64(dst[0].SystemDriverResidentBytes),
)
ch <- prometheus.MustNewConstMetric(
c.SystemDriverTotalBytes,
prometheus.GaugeValue,
float64(dst[0].SystemDriverTotalBytes),
)
ch <- prometheus.MustNewConstMetric(
c.TransitionFaultsTotal,
prometheus.GaugeValue,
float64(dst[0].TransitionFaultsPersec),
)
ch <- prometheus.MustNewConstMetric(
c.TransitionPagesRepurposedTotal,
prometheus.GaugeValue,
float64(dst[0].TransitionPagesRePurposedPersec),
)
ch <- prometheus.MustNewConstMetric(
c.WriteCopiesTotal,
prometheus.GaugeValue,
float64(dst[0].WriteCopiesPersec),
)
return nil, nil
}

127
collector/msmq.go Normal file
View File

@@ -0,0 +1,127 @@
// +build windows
package collector
import (
"strings"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"gopkg.in/alecthomas/kingpin.v2"
)
func init() {
Factories["msmq"] = NewMSMQCollector
}
var (
msmqWhereClause = kingpin.Flag("collector.msmq.msmq-where", "WQL 'where' clause to use in WMI metrics query. Limits the response to the msmqs you specify and reduces the size of the response.").String()
)
// A Win32_PerfRawData_MSMQ_MSMQQueueCollector is a Prometheus collector for WMI Win32_PerfRawData_MSMQ_MSMQQueue metrics
type Win32_PerfRawData_MSMQ_MSMQQueueCollector struct {
BytesinJournalQueue *prometheus.Desc
BytesinQueue *prometheus.Desc
MessagesinJournalQueue *prometheus.Desc
MessagesinQueue *prometheus.Desc
queryWhereClause string
}
// NewWin32_PerfRawData_MSMQ_MSMQQueueCollector ...
func NewMSMQCollector() (Collector, error) {
const subsystem = "msmq"
if *msmqWhereClause != "" {
log.Warn("No where-clause specified for msmq collector. This will generate a very large number of metrics!")
}
return &Win32_PerfRawData_MSMQ_MSMQQueueCollector{
BytesinJournalQueue: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "bytes_in_journal_queue"),
"Size of queue journal in bytes",
[]string{"name"},
nil,
),
BytesinQueue: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "bytes_in_queue"),
"Size of queue in bytes",
[]string{"name"},
nil,
),
MessagesinJournalQueue: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "messages_in_journal_queue"),
"Count messages in queue journal",
[]string{"name"},
nil,
),
MessagesinQueue: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "messages_in_queue"),
"Count messages in queue",
[]string{"name"},
nil,
),
queryWhereClause: *msmqWhereClause,
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Win32_PerfRawData_MSMQ_MSMQQueueCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting msmq metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_MSMQ_MSMQQueue struct {
Name string
BytesinJournalQueue uint64
BytesinQueue uint64
MessagesinJournalQueue uint64
MessagesinQueue uint64
}
func (c *Win32_PerfRawData_MSMQ_MSMQQueueCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_MSMQ_MSMQQueue
q := queryAllWhere(&dst, c.queryWhereClause)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
for _, msmq := range dst {
if msmq.Name == "Computer Queues" {
continue
}
ch <- prometheus.MustNewConstMetric(
c.BytesinJournalQueue,
prometheus.GaugeValue,
float64(msmq.BytesinJournalQueue),
strings.ToLower(msmq.Name),
)
ch <- prometheus.MustNewConstMetric(
c.BytesinQueue,
prometheus.GaugeValue,
float64(msmq.BytesinQueue),
strings.ToLower(msmq.Name),
)
ch <- prometheus.MustNewConstMetric(
c.MessagesinJournalQueue,
prometheus.GaugeValue,
float64(msmq.MessagesinJournalQueue),
strings.ToLower(msmq.Name),
)
ch <- prometheus.MustNewConstMetric(
c.MessagesinQueue,
prometheus.GaugeValue,
float64(msmq.MessagesinQueue),
strings.ToLower(msmq.Name),
)
}
return nil, nil
}

3560
collector/mssql.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,15 @@
// returns data points from Win32_PerfRawData_Tcpip_NetworkInterface
// https://technet.microsoft.com/en-us/security/aa394340(v=vs.80) (Win32_PerfRawData_Tcpip_NetworkInterface class)
// https://msdn.microsoft.com/en-us/library/aa394216 (Win32_NetworkAdapter class)
// https://msdn.microsoft.com/en-us/library/aa394353 (Win32_PnPEntity class)
// +build windows
package collector
import (
"flag"
"fmt"
"log"
"regexp"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"gopkg.in/alecthomas/kingpin.v2"
)
func init() {
@@ -21,8 +17,14 @@ func init() {
}
var (
nicWhitelist = flag.String("collector.net.nic-whitelist", ".+", "Regexp of NIC:s to whitelist. NIC name must both match whitelist and not match blacklist to be included.")
nicBlacklist = flag.String("collector.net.nic-blacklist", "", "Regexp of NIC:s to blacklist. NIC name must both match whitelist and not match blacklist to be included.")
nicWhitelist = kingpin.Flag(
"collector.net.nic-whitelist",
"Regexp of NIC:s to whitelist. NIC name must both match whitelist and not match blacklist to be included.",
).Default(".+").String()
nicBlacklist = kingpin.Flag(
"collector.net.nic-blacklist",
"Regexp of NIC:s to blacklist. NIC name must both match whitelist and not match blacklist to be included.",
).Default("").String()
nicNameToUnderscore = regexp.MustCompile("[^a-zA-Z0-9]")
)
@@ -39,6 +41,7 @@ type NetworkCollector struct {
PacketsReceivedTotal *prometheus.Desc
PacketsReceivedUnknown *prometheus.Desc
PacketsSentTotal *prometheus.Desc
CurrentBandwidth *prometheus.Desc
nicWhitelistPattern *regexp.Regexp
nicBlacklistPattern *regexp.Regexp
@@ -115,6 +118,12 @@ func NewNetworkCollector() (Collector, error) {
[]string{"nic"},
nil,
),
CurrentBandwidth: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "current_bandwidth"),
"(Network.CurrentBandwidth)",
[]string{"nic"},
nil,
),
nicWhitelistPattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *nicWhitelist)),
nicBlacklistPattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *nicBlacklist)),
@@ -125,7 +134,7 @@ func NewNetworkCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *NetworkCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Println("[ERROR] failed collecting net metrics:", desc, err)
log.Error("failed collecting net metrics:", desc, err)
return err
}
return nil
@@ -137,6 +146,8 @@ func mangleNetworkName(name string) string {
return nicNameToUnderscore.ReplaceAllString(name, "_")
}
// Win32_PerfRawData_Tcpip_NetworkInterface docs:
// - https://technet.microsoft.com/en-us/security/aa394340(v=vs.80)
type Win32_PerfRawData_Tcpip_NetworkInterface struct {
BytesReceivedPerSec uint64
BytesSentPerSec uint64
@@ -150,12 +161,13 @@ type Win32_PerfRawData_Tcpip_NetworkInterface struct {
PacketsReceivedPerSec uint64
PacketsReceivedUnknown uint64
PacketsSentPerSec uint64
CurrentBandwidth uint64
}
func (c *NetworkCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_Tcpip_NetworkInterface
q := wmi.CreateQuery(&dst, "")
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
@@ -238,6 +250,12 @@ func (c *NetworkCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Des
float64(nic.PacketsSentPerSec),
name,
)
ch <- prometheus.MustNewConstMetric(
c.CurrentBandwidth,
prometheus.CounterValue,
float64(nic.CurrentBandwidth),
name,
)
}
return nil, nil

View File

@@ -1,3 +1,5 @@
// +build windows
package collector
import "testing"

View File

@@ -0,0 +1,117 @@
// +build windows
package collector
import (
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["netframework_clrexceptions"] = NewNETFramework_NETCLRExceptionsCollector
}
// A NETFramework_NETCLRExceptionsCollector is a Prometheus collector for WMI Win32_PerfRawData_NETFramework_NETCLRExceptions metrics
type NETFramework_NETCLRExceptionsCollector struct {
NumberofExcepsThrown *prometheus.Desc
NumberofFilters *prometheus.Desc
NumberofFinallys *prometheus.Desc
ThrowToCatchDepth *prometheus.Desc
}
// NewNETFramework_NETCLRExceptionsCollector ...
func NewNETFramework_NETCLRExceptionsCollector() (Collector, error) {
const subsystem = "netframework_clrexceptions"
return &NETFramework_NETCLRExceptionsCollector{
NumberofExcepsThrown: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "exceptions_thrown_total"),
"Displays the total number of exceptions thrown since the application started. This includes both .NET exceptions and unmanaged exceptions that are converted into .NET exceptions.",
[]string{"process"},
nil,
),
NumberofFilters: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "exceptions_filters_total"),
"Displays the total number of .NET exception filters executed. An exception filter evaluates regardless of whether an exception is handled.",
[]string{"process"},
nil,
),
NumberofFinallys: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "exceptions_finallys_total"),
"Displays the total number of finally blocks executed. Only the finally blocks executed for an exception are counted; finally blocks on normal code paths are not counted by this counter.",
[]string{"process"},
nil,
),
ThrowToCatchDepth: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "throw_to_catch_depth_total"),
"Displays the total number of stack frames traversed, from the frame that threw the exception to the frame that handled the exception.",
[]string{"process"},
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *NETFramework_NETCLRExceptionsCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting win32_perfrawdata_netframework_netclrexceptions metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_NETFramework_NETCLRExceptions struct {
Name string
NumberofExcepsThrown uint32
NumberofExcepsThrownPersec uint32
NumberofFiltersPersec uint32
NumberofFinallysPersec uint32
ThrowToCatchDepthPersec uint32
}
func (c *NETFramework_NETCLRExceptionsCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_NETFramework_NETCLRExceptions
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
for _, process := range dst {
if process.Name == "_Global_" {
continue
}
ch <- prometheus.MustNewConstMetric(
c.NumberofExcepsThrown,
prometheus.CounterValue,
float64(process.NumberofExcepsThrown),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.NumberofFilters,
prometheus.CounterValue,
float64(process.NumberofFiltersPersec),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.NumberofFinallys,
prometheus.CounterValue,
float64(process.NumberofFinallysPersec),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.ThrowToCatchDepth,
prometheus.CounterValue,
float64(process.ThrowToCatchDepthPersec),
process.Name,
)
}
return nil, nil
}

View File

@@ -0,0 +1,103 @@
// +build windows
package collector
import (
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["netframework_clrinterop"] = NewNETFramework_NETCLRInteropCollector
}
// A NETFramework_NETCLRInteropCollector is a Prometheus collector for WMI Win32_PerfRawData_NETFramework_NETCLRInterop metrics
type NETFramework_NETCLRInteropCollector struct {
NumberofCCWs *prometheus.Desc
Numberofmarshalling *prometheus.Desc
NumberofStubs *prometheus.Desc
}
// NewNETFramework_NETCLRInteropCollector ...
func NewNETFramework_NETCLRInteropCollector() (Collector, error) {
const subsystem = "netframework_clrinterop"
return &NETFramework_NETCLRInteropCollector{
NumberofCCWs: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "com_callable_wrappers_total"),
"Displays the current number of COM callable wrappers (CCWs). A CCW is a proxy for a managed object being referenced from an unmanaged COM client.",
[]string{"process"},
nil,
),
Numberofmarshalling: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "interop_marshalling_total"),
"Displays the total number of times arguments and return values have been marshaled from managed to unmanaged code, and vice versa, since the application started.",
[]string{"process"},
nil,
),
NumberofStubs: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "interop_stubs_created_total"),
"Displays the current number of stubs created by the common language runtime. Stubs are responsible for marshaling arguments and return values from managed to unmanaged code, and vice versa, during a COM interop call or a platform invoke call.",
[]string{"process"},
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *NETFramework_NETCLRInteropCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting win32_perfrawdata_netframework_netclrinterop metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_NETFramework_NETCLRInterop struct {
Name string
NumberofCCWs uint32
Numberofmarshalling uint32
NumberofStubs uint32
NumberofTLBexportsPersec uint32
NumberofTLBimportsPersec uint32
}
func (c *NETFramework_NETCLRInteropCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_NETFramework_NETCLRInterop
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
for _, process := range dst {
if process.Name == "_Global_" {
continue
}
ch <- prometheus.MustNewConstMetric(
c.NumberofCCWs,
prometheus.CounterValue,
float64(process.NumberofCCWs),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.Numberofmarshalling,
prometheus.CounterValue,
float64(process.Numberofmarshalling),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.NumberofStubs,
prometheus.CounterValue,
float64(process.NumberofStubs),
process.Name,
)
}
return nil, nil
}

View File

@@ -0,0 +1,119 @@
// +build windows
package collector
import (
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["netframework_clrjit"] = NewNETFramework_NETCLRJitCollector
}
// A NETFramework_NETCLRJitCollector is a Prometheus collector for WMI Win32_PerfRawData_NETFramework_NETCLRJit metrics
type NETFramework_NETCLRJitCollector struct {
NumberofMethodsJitted *prometheus.Desc
TimeinJit *prometheus.Desc
StandardJitFailures *prometheus.Desc
TotalNumberofILBytesJitted *prometheus.Desc
}
// NewNETFramework_NETCLRJitCollector ...
func NewNETFramework_NETCLRJitCollector() (Collector, error) {
const subsystem = "netframework_clrjit"
return &NETFramework_NETCLRJitCollector{
NumberofMethodsJitted: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "jit_methods_total"),
"Displays the total number of methods JIT-compiled since the application started. This counter does not include pre-JIT-compiled methods.",
[]string{"process"},
nil,
),
TimeinJit: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "jit_time_percent"),
"Displays the percentage of time spent in JIT compilation. This counter is updated at the end of every JIT compilation phase. A JIT compilation phase occurs when a method and its dependencies are compiled.",
[]string{"process"},
nil,
),
StandardJitFailures: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "jit_standard_failures_total"),
"Displays the peak number of methods the JIT compiler has failed to compile since the application started. This failure can occur if the MSIL cannot be verified or if there is an internal error in the JIT compiler.",
[]string{"process"},
nil,
),
TotalNumberofILBytesJitted: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "jit_il_bytes_total"),
"Displays the total number of Microsoft intermediate language (MSIL) bytes compiled by the just-in-time (JIT) compiler since the application started",
[]string{"process"},
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *NETFramework_NETCLRJitCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting win32_perfrawdata_netframework_netclrjit metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_NETFramework_NETCLRJit struct {
Name string
Frequency_PerfTime uint32
ILBytesJittedPersec uint32
NumberofILBytesJitted uint32
NumberofMethodsJitted uint32
PercentTimeinJit uint32
StandardJitFailures uint32
TotalNumberofILBytesJitted uint32
}
func (c *NETFramework_NETCLRJitCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_NETFramework_NETCLRJit
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
for _, process := range dst {
if process.Name == "_Global_" {
continue
}
ch <- prometheus.MustNewConstMetric(
c.NumberofMethodsJitted,
prometheus.CounterValue,
float64(process.NumberofMethodsJitted),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TimeinJit,
prometheus.GaugeValue,
float64(process.PercentTimeinJit)/float64(process.Frequency_PerfTime),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.StandardJitFailures,
prometheus.GaugeValue,
float64(process.StandardJitFailures),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TotalNumberofILBytesJitted,
prometheus.CounterValue,
float64(process.TotalNumberofILBytesJitted),
process.Name,
)
}
return nil, nil
}

View File

@@ -0,0 +1,198 @@
// +build windows
package collector
import (
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["netframework_clrloading"] = NewNETFramework_NETCLRLoadingCollector
}
// A NETFramework_NETCLRLoadingCollector is a Prometheus collector for WMI Win32_PerfRawData_NETFramework_NETCLRLoading metrics
type NETFramework_NETCLRLoadingCollector struct {
BytesinLoaderHeap *prometheus.Desc
Currentappdomains *prometheus.Desc
CurrentAssemblies *prometheus.Desc
CurrentClassesLoaded *prometheus.Desc
TotalAppdomains *prometheus.Desc
Totalappdomainsunloaded *prometheus.Desc
TotalAssemblies *prometheus.Desc
TotalClassesLoaded *prometheus.Desc
TotalNumberofLoadFailures *prometheus.Desc
}
// NewNETFramework_NETCLRLoadingCollector ...
func NewNETFramework_NETCLRLoadingCollector() (Collector, error) {
const subsystem = "netframework_clrloading"
return &NETFramework_NETCLRLoadingCollector{
BytesinLoaderHeap: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "loader_heap_size_bytes"),
"Displays the current size, in bytes, of the memory committed by the class loader across all application domains. Committed memory is the physical space reserved in the disk paging file.",
[]string{"process"},
nil,
),
Currentappdomains: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "appdomains_loaded_current"),
"Displays the current number of application domains loaded in this application.",
[]string{"process"},
nil,
),
CurrentAssemblies: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "assemblies_loaded_current"),
"Displays the current number of assemblies loaded across all application domains in the currently running application. If the assembly is loaded as domain-neutral from multiple application domains, this counter is incremented only once.",
[]string{"process"},
nil,
),
CurrentClassesLoaded: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "classes_loaded_current"),
"Displays the current number of classes loaded in all assemblies.",
[]string{"process"},
nil,
),
TotalAppdomains: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "appdomains_loaded_total"),
"Displays the peak number of application domains loaded since the application started.",
[]string{"process"},
nil,
),
Totalappdomainsunloaded: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "appdomains_unloaded_total"),
"Displays the total number of application domains unloaded since the application started. If an application domain is loaded and unloaded multiple times, this counter increments each time the application domain is unloaded.",
[]string{"process"},
nil,
),
TotalAssemblies: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "assemblies_loaded_total"),
"Displays the total number of assemblies loaded since the application started. If the assembly is loaded as domain-neutral from multiple application domains, this counter is incremented only once.",
[]string{"process"},
nil,
),
TotalClassesLoaded: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "classes_loaded_total"),
"Displays the cumulative number of classes loaded in all assemblies since the application started.",
[]string{"process"},
nil,
),
TotalNumberofLoadFailures: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "class_load_failures_total"),
"Displays the peak number of classes that have failed to load since the application started.",
[]string{"process"},
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *NETFramework_NETCLRLoadingCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting win32_perfrawdata_netframework_netclrloading metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_NETFramework_NETCLRLoading struct {
Name string
AssemblySearchLength uint32
BytesinLoaderHeap uint64
Currentappdomains uint32
CurrentAssemblies uint32
CurrentClassesLoaded uint32
PercentTimeLoading uint64
Rateofappdomains uint32
Rateofappdomainsunloaded uint32
RateofAssemblies uint32
RateofClassesLoaded uint32
RateofLoadFailures uint32
TotalAppdomains uint32
Totalappdomainsunloaded uint32
TotalAssemblies uint32
TotalClassesLoaded uint32
TotalNumberofLoadFailures uint32
}
func (c *NETFramework_NETCLRLoadingCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_NETFramework_NETCLRLoading
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
for _, process := range dst {
if process.Name == "_Global_" {
continue
}
ch <- prometheus.MustNewConstMetric(
c.BytesinLoaderHeap,
prometheus.GaugeValue,
float64(process.BytesinLoaderHeap),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.Currentappdomains,
prometheus.GaugeValue,
float64(process.Currentappdomains),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.CurrentAssemblies,
prometheus.GaugeValue,
float64(process.CurrentAssemblies),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.CurrentClassesLoaded,
prometheus.GaugeValue,
float64(process.CurrentClassesLoaded),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TotalAppdomains,
prometheus.CounterValue,
float64(process.TotalAppdomains),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.Totalappdomainsunloaded,
prometheus.CounterValue,
float64(process.Totalappdomainsunloaded),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TotalAssemblies,
prometheus.CounterValue,
float64(process.TotalAssemblies),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TotalClassesLoaded,
prometheus.CounterValue,
float64(process.TotalClassesLoaded),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TotalNumberofLoadFailures,
prometheus.CounterValue,
float64(process.TotalNumberofLoadFailures),
process.Name,
)
}
return nil, nil
}

View File

@@ -0,0 +1,164 @@
// +build windows
package collector
import (
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["netframework_clrlocksandthreads"] = NewNETFramework_NETCLRLocksAndThreadsCollector
}
// A NETFramework_NETCLRLocksAndThreadsCollector is a Prometheus collector for WMI Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads metrics
type NETFramework_NETCLRLocksAndThreadsCollector struct {
CurrentQueueLength *prometheus.Desc
NumberofcurrentlogicalThreads *prometheus.Desc
NumberofcurrentphysicalThreads *prometheus.Desc
Numberofcurrentrecognizedthreads *prometheus.Desc
Numberoftotalrecognizedthreads *prometheus.Desc
QueueLengthPeak *prometheus.Desc
TotalNumberofContentions *prometheus.Desc
}
// NewNETFramework_NETCLRLocksAndThreadsCollector ...
func NewNETFramework_NETCLRLocksAndThreadsCollector() (Collector, error) {
const subsystem = "netframework_clrlocksandthreads"
return &NETFramework_NETCLRLocksAndThreadsCollector{
CurrentQueueLength: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "current_queue_length"),
"Displays the total number of threads that are currently waiting to acquire a managed lock in the application.",
[]string{"process"},
nil,
),
NumberofcurrentlogicalThreads: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "current_logical_threads"),
"Displays the number of current managed thread objects in the application. This counter maintains the count of both running and stopped threads. ",
[]string{"process"},
nil,
),
NumberofcurrentphysicalThreads: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "physical_threads_current"),
"Displays the number of native operating system threads created and owned by the common language runtime to act as underlying threads for managed thread objects. This counter's value does not include the threads used by the runtime in its internal operations; it is a subset of the threads in the operating system process.",
[]string{"process"},
nil,
),
Numberofcurrentrecognizedthreads: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "recognized_threads_current"),
"Displays the number of threads that are currently recognized by the runtime. These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once.",
[]string{"process"},
nil,
),
Numberoftotalrecognizedthreads: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "recognized_threads_total"),
"Displays the total number of threads that have been recognized by the runtime since the application started. These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once.",
[]string{"process"},
nil,
),
QueueLengthPeak: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "queue_length_total"),
"Displays the total number of threads that waited to acquire a managed lock since the application started.",
[]string{"process"},
nil,
),
TotalNumberofContentions: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "contentions_total"),
"Displays the total number of times that threads in the runtime have attempted to acquire a managed lock unsuccessfully.",
[]string{"process"},
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *NETFramework_NETCLRLocksAndThreadsCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting win32_perfrawdata_netframework_netclrlocksandthreads metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads struct {
Name string
ContentionRatePersec uint32
CurrentQueueLength uint32
NumberofcurrentlogicalThreads uint32
NumberofcurrentphysicalThreads uint32
Numberofcurrentrecognizedthreads uint32
Numberoftotalrecognizedthreads uint32
QueueLengthPeak uint32
QueueLengthPersec uint32
RateOfRecognizedThreadsPersec uint32
TotalNumberofContentions uint32
}
func (c *NETFramework_NETCLRLocksAndThreadsCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
for _, process := range dst {
if process.Name == "_Global_" {
continue
}
ch <- prometheus.MustNewConstMetric(
c.CurrentQueueLength,
prometheus.GaugeValue,
float64(process.CurrentQueueLength),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.NumberofcurrentlogicalThreads,
prometheus.GaugeValue,
float64(process.NumberofcurrentlogicalThreads),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.NumberofcurrentphysicalThreads,
prometheus.GaugeValue,
float64(process.NumberofcurrentphysicalThreads),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.Numberofcurrentrecognizedthreads,
prometheus.GaugeValue,
float64(process.Numberofcurrentrecognizedthreads),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.Numberoftotalrecognizedthreads,
prometheus.CounterValue,
float64(process.Numberoftotalrecognizedthreads),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.QueueLengthPeak,
prometheus.CounterValue,
float64(process.QueueLengthPeak),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TotalNumberofContentions,
prometheus.CounterValue,
float64(process.TotalNumberofContentions),
process.Name,
)
}
return nil, nil
}

View File

@@ -0,0 +1,302 @@
// +build windows
package collector
import (
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["netframework_clrmemory"] = NewNETFramework_NETCLRMemoryCollector
}
// A NETFramework_NETCLRMemoryCollector is a Prometheus collector for WMI Win32_PerfRawData_NETFramework_NETCLRMemory metrics
type NETFramework_NETCLRMemoryCollector struct {
AllocatedBytes *prometheus.Desc
FinalizationSurvivors *prometheus.Desc
HeapSize *prometheus.Desc
PromotedBytes *prometheus.Desc
NumberGCHandles *prometheus.Desc
NumberCollections *prometheus.Desc
NumberInducedGC *prometheus.Desc
NumberofPinnedObjects *prometheus.Desc
NumberofSinkBlocksinuse *prometheus.Desc
NumberTotalCommittedBytes *prometheus.Desc
NumberTotalreservedBytes *prometheus.Desc
TimeinGC *prometheus.Desc
PromotedFinalizationMemoryfromGen0 *prometheus.Desc
PromotedMemoryfromGen0 *prometheus.Desc
PromotedMemoryfromGen1 *prometheus.Desc
}
// NewNETFramework_NETCLRMemoryCollector ...
func NewNETFramework_NETCLRMemoryCollector() (Collector, error) {
const subsystem = "netframework_clrmemory"
return &NETFramework_NETCLRMemoryCollector{
AllocatedBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "allocated_bytes_total"),
"Displays the total number of bytes allocated on the garbage collection heap.",
[]string{"process"},
nil,
),
FinalizationSurvivors: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "finalization_survivors"),
"Displays the number of garbage-collected objects that survive a collection because they are waiting to be finalized.",
[]string{"process"},
nil,
),
HeapSize: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "heap_size_bytes"),
"Displays the maximum bytes that can be allocated; it does not indicate the current number of bytes allocated.",
[]string{"process", "area"},
nil,
),
PromotedBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "promoted_bytes"),
"Displays the bytes that were promoted from the generation to the next one during the last GC. Memory is promoted when it survives a garbage collection.",
[]string{"process", "area"},
nil,
),
NumberGCHandles: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "number_gc_handles"),
"Displays the current number of garbage collection handles in use. Garbage collection handles are handles to resources external to the common language runtime and the managed environment.",
[]string{"process"},
nil,
),
NumberCollections: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "collections_total"),
"Displays the number of times the generation objects are garbage collected since the application started.",
[]string{"process", "area"},
nil,
),
NumberInducedGC: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "induced_gc_total"),
"Displays the peak number of times garbage collection was performed because of an explicit call to GC.Collect.",
[]string{"process"},
nil,
),
NumberofPinnedObjects: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "number_pinned_objects"),
"Displays the number of pinned objects encountered in the last garbage collection.",
[]string{"process"},
nil,
),
NumberofSinkBlocksinuse: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "number_sink_blocksinuse"),
"Displays the current number of synchronization blocks in use. Synchronization blocks are per-object data structures allocated for storing synchronization information. They hold weak references to managed objects and must be scanned by the garbage collector.",
[]string{"process"},
nil,
),
NumberTotalCommittedBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "committed_bytes"),
"Displays the amount of virtual memory, in bytes, currently committed by the garbage collector. Committed memory is the physical memory for which space has been reserved in the disk paging file.",
[]string{"process"},
nil,
),
NumberTotalreservedBytes: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "reserved_bytes"),
"Displays the amount of virtual memory, in bytes, currently reserved by the garbage collector. Reserved memory is the virtual memory space reserved for the application when no disk or main memory pages have been used.",
[]string{"process"},
nil,
),
TimeinGC: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "gc_time_percent"),
"Displays the percentage of time that was spent performing a garbage collection in the last sample.",
[]string{"process"},
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *NETFramework_NETCLRMemoryCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting win32_perfrawdata_netframework_netclrmemory metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_NETFramework_NETCLRMemory struct {
Name string
AllocatedBytesPersec uint64
FinalizationSurvivors uint64
Frequency_PerfTime uint64
Gen0heapsize uint64
Gen0PromotedBytesPerSec uint64
Gen1heapsize uint64
Gen1PromotedBytesPerSec uint64
Gen2heapsize uint64
LargeObjectHeapsize uint64
NumberBytesinallHeaps uint64
NumberGCHandles uint64
NumberGen0Collections uint64
NumberGen1Collections uint64
NumberGen2Collections uint64
NumberInducedGC uint64
NumberofPinnedObjects uint64
NumberofSinkBlocksinuse uint64
NumberTotalcommittedBytes uint64
NumberTotalreservedBytes uint64
PercentTimeinGC uint32
ProcessID uint64
PromotedFinalizationMemoryfromGen0 uint64
PromotedMemoryfromGen0 uint64
PromotedMemoryfromGen1 uint64
}
func (c *NETFramework_NETCLRMemoryCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_NETFramework_NETCLRMemory
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
for _, process := range dst {
if process.Name == "_Global_" {
continue
}
ch <- prometheus.MustNewConstMetric(
c.AllocatedBytes,
prometheus.CounterValue,
float64(process.AllocatedBytesPersec),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.FinalizationSurvivors,
prometheus.GaugeValue,
float64(process.FinalizationSurvivors),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.HeapSize,
prometheus.GaugeValue,
float64(process.Gen0heapsize),
process.Name,
"Gen0",
)
ch <- prometheus.MustNewConstMetric(
c.PromotedBytes,
prometheus.GaugeValue,
float64(process.Gen0PromotedBytesPerSec),
process.Name,
"Gen0",
)
ch <- prometheus.MustNewConstMetric(
c.HeapSize,
prometheus.GaugeValue,
float64(process.Gen1heapsize),
process.Name,
"Gen1",
)
ch <- prometheus.MustNewConstMetric(
c.PromotedBytes,
prometheus.GaugeValue,
float64(process.Gen1PromotedBytesPerSec),
process.Name,
"Gen1",
)
ch <- prometheus.MustNewConstMetric(
c.HeapSize,
prometheus.GaugeValue,
float64(process.Gen2heapsize),
process.Name,
"Gen2",
)
ch <- prometheus.MustNewConstMetric(
c.HeapSize,
prometheus.GaugeValue,
float64(process.LargeObjectHeapsize),
process.Name,
"LOH",
)
ch <- prometheus.MustNewConstMetric(
c.NumberGCHandles,
prometheus.GaugeValue,
float64(process.NumberGCHandles),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.NumberCollections,
prometheus.CounterValue,
float64(process.NumberGen0Collections),
process.Name,
"Gen0",
)
ch <- prometheus.MustNewConstMetric(
c.NumberCollections,
prometheus.CounterValue,
float64(process.NumberGen1Collections),
process.Name,
"Gen1",
)
ch <- prometheus.MustNewConstMetric(
c.NumberCollections,
prometheus.CounterValue,
float64(process.NumberGen2Collections),
process.Name,
"Gen2",
)
ch <- prometheus.MustNewConstMetric(
c.NumberInducedGC,
prometheus.CounterValue,
float64(process.NumberInducedGC),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.NumberofPinnedObjects,
prometheus.GaugeValue,
float64(process.NumberofPinnedObjects),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.NumberofSinkBlocksinuse,
prometheus.GaugeValue,
float64(process.NumberofSinkBlocksinuse),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.NumberTotalCommittedBytes,
prometheus.GaugeValue,
float64(process.NumberTotalcommittedBytes),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.NumberTotalreservedBytes,
prometheus.GaugeValue,
float64(process.NumberTotalreservedBytes),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TimeinGC,
prometheus.GaugeValue,
float64(process.PercentTimeinGC)/float64(process.Frequency_PerfTime),
process.Name,
)
}
return nil, nil
}

View File

@@ -0,0 +1,147 @@
// +build windows
package collector
import (
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["netframework_clrremoting"] = NewNETFramework_NETCLRRemotingCollector
}
// A NETFramework_NETCLRRemotingCollector is a Prometheus collector for WMI Win32_PerfRawData_NETFramework_NETCLRRemoting metrics
type NETFramework_NETCLRRemotingCollector struct {
Channels *prometheus.Desc
ContextBoundClassesLoaded *prometheus.Desc
ContextBoundObjects *prometheus.Desc
ContextProxies *prometheus.Desc
Contexts *prometheus.Desc
TotalRemoteCalls *prometheus.Desc
}
// NewNETFramework_NETCLRRemotingCollector ...
func NewNETFramework_NETCLRRemotingCollector() (Collector, error) {
const subsystem = "netframework_clrremoting"
return &NETFramework_NETCLRRemotingCollector{
Channels: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "channels_total"),
"Displays the total number of remoting channels registered across all application domains since application started.",
[]string{"process"},
nil,
),
ContextBoundClassesLoaded: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "context_bound_classes_loaded"),
"Displays the current number of context-bound classes that are loaded.",
[]string{"process"},
nil,
),
ContextBoundObjects: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "context_bound_objects_total"),
"Displays the total number of context-bound objects allocated.",
[]string{"process"},
nil,
),
ContextProxies: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "context_proxies_total"),
"Displays the total number of remoting proxy objects in this process since it started.",
[]string{"process"},
nil,
),
Contexts: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "contexts"),
"Displays the current number of remoting contexts in the application.",
[]string{"process"},
nil,
),
TotalRemoteCalls: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "remote_calls_total"),
"Displays the total number of remote procedure calls invoked since the application started.",
[]string{"process"},
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *NETFramework_NETCLRRemotingCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting win32_perfrawdata_netframework_netclrremoting metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_NETFramework_NETCLRRemoting struct {
Name string
Channels uint32
ContextBoundClassesLoaded uint32
ContextBoundObjectsAllocPersec uint32
ContextProxies uint32
Contexts uint32
RemoteCallsPersec uint32
TotalRemoteCalls uint32
}
func (c *NETFramework_NETCLRRemotingCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_NETFramework_NETCLRRemoting
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
for _, process := range dst {
if process.Name == "_Global_" {
continue
}
ch <- prometheus.MustNewConstMetric(
c.Channels,
prometheus.CounterValue,
float64(process.Channels),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.ContextBoundClassesLoaded,
prometheus.GaugeValue,
float64(process.ContextBoundClassesLoaded),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.ContextBoundObjects,
prometheus.CounterValue,
float64(process.ContextBoundObjectsAllocPersec),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.ContextProxies,
prometheus.CounterValue,
float64(process.ContextProxies),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.Contexts,
prometheus.GaugeValue,
float64(process.Contexts),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TotalRemoteCalls,
prometheus.CounterValue,
float64(process.TotalRemoteCalls),
process.Name,
)
}
return nil, nil
}

View File

@@ -0,0 +1,118 @@
// +build windows
package collector
import (
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["netframework_clrsecurity"] = NewNETFramework_NETCLRSecurityCollector
}
// A NETFramework_NETCLRSecurityCollector is a Prometheus collector for WMI Win32_PerfRawData_NETFramework_NETCLRSecurity metrics
type NETFramework_NETCLRSecurityCollector struct {
NumberLinkTimeChecks *prometheus.Desc
TimeinRTchecks *prometheus.Desc
StackWalkDepth *prometheus.Desc
TotalRuntimeChecks *prometheus.Desc
}
// NewNETFramework_NETCLRSecurityCollector ...
func NewNETFramework_NETCLRSecurityCollector() (Collector, error) {
const subsystem = "netframework_clrsecurity"
return &NETFramework_NETCLRSecurityCollector{
NumberLinkTimeChecks: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "link_time_checks_total"),
"Displays the total number of link-time code access security checks since the application started.",
[]string{"process"},
nil,
),
TimeinRTchecks: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "rt_checks_time_percent"),
"Displays the percentage of time spent performing runtime code access security checks in the last sample.",
[]string{"process"},
nil,
),
StackWalkDepth: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "stack_walk_depth"),
"Displays the depth of the stack during that last runtime code access security check.",
[]string{"process"},
nil,
),
TotalRuntimeChecks: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "runtime_checks_total"),
"Displays the total number of runtime code access security checks performed since the application started.",
[]string{"process"},
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *NETFramework_NETCLRSecurityCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting win32_perfrawdata_netframework_netclrsecurity metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_NETFramework_NETCLRSecurity struct {
Name string
Frequency_PerfTime uint32
NumberLinkTimeChecks uint32
PercentTimeinRTchecks uint32
PercentTimeSigAuthenticating uint64
StackWalkDepth uint32
TotalRuntimeChecks uint32
}
func (c *NETFramework_NETCLRSecurityCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_NETFramework_NETCLRSecurity
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
for _, process := range dst {
if process.Name == "_Global_" {
continue
}
ch <- prometheus.MustNewConstMetric(
c.NumberLinkTimeChecks,
prometheus.CounterValue,
float64(process.NumberLinkTimeChecks),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TimeinRTchecks,
prometheus.GaugeValue,
float64(process.PercentTimeinRTchecks)/float64(process.Frequency_PerfTime),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.StackWalkDepth,
prometheus.GaugeValue,
float64(process.StackWalkDepth),
process.Name,
)
ch <- prometheus.MustNewConstMetric(
c.TotalRuntimeChecks,
prometheus.CounterValue,
float64(process.TotalRuntimeChecks),
process.Name,
)
}
return nil, nil
}

View File

@@ -1,14 +1,14 @@
// returns data points from Win32_OperatingSystem
// https://msdn.microsoft.com/en-us/library/aa394239 - Win32_OperatingSystem class
// +build windows
package collector
import (
"log"
"errors"
"time"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
@@ -27,8 +27,8 @@ type OSCollector struct {
PagingLimitBytes *prometheus.Desc
VirtualMemoryBytes *prometheus.Desc
VisibleMemoryBytes *prometheus.Desc
Time *prometheus.Desc
Timezone *prometheus.Desc
Time *prometheus.Desc
Timezone *prometheus.Desc
}
// NewOSCollector ...
@@ -115,12 +115,14 @@ func NewOSCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *OSCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Println("[ERROR] failed collecting os metrics:", desc, err)
log.Error("failed collecting os metrics:", desc, err)
return err
}
return nil
}
// Win32_OperatingSystem docs:
// - https://msdn.microsoft.com/en-us/library/aa394239 - Win32_OperatingSystem class
type Win32_OperatingSystem struct {
FreePhysicalMemory uint64
FreeSpaceInPagingFiles uint64
@@ -132,15 +134,20 @@ type Win32_OperatingSystem struct {
SizeStoredInPagingFiles uint64
TotalVirtualMemorySize uint64
TotalVisibleMemorySize uint64
LocalDateTime time.Time
LocalDateTime time.Time
}
func (c *OSCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_OperatingSystem
if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil {
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
if len(dst) == 0 {
return nil, errors.New("WMI query returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.PhysicalMemoryFreeBytes,
prometheus.GaugeValue,
@@ -148,7 +155,7 @@ func (c *OSCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, er
)
time := dst[0].LocalDateTime
ch <- prometheus.MustNewConstMetric(
c.Time,
prometheus.GaugeValue,

View File

@@ -1,16 +1,15 @@
// returns data points from Win32_PerfRawData_PerfProc_Process
// https://msdn.microsoft.com/en-us/library/aa394323(v=vs.85).aspx - Win32_PerfRawData_PerfProc_Process class
// +build windows
package collector
import (
"bytes"
"flag"
"strconv"
"strings"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"gopkg.in/alecthomas/kingpin.v2"
)
func init() {
@@ -18,7 +17,10 @@ func init() {
}
var (
processWhereClause = flag.String("collector.process.processes-where", "", "WQL 'where' clause to use in WMI metrics query. Limits the response to the processes you specify and reduces the size of the response.")
processWhereClause = kingpin.Flag(
"collector.process.processes-where",
"WQL 'where' clause to use in WMI metrics query. Limits the response to the processes you specify and reduces the size of the response.",
).Default("").String()
)
// A ProcessCollector is a Prometheus collector for WMI Win32_PerfRawData_PerfProc_Process metrics
@@ -44,11 +46,7 @@ type ProcessCollector struct {
func NewProcessCollector() (Collector, error) {
const subsystem = "process"
var wc bytes.Buffer
if *processWhereClause != "" {
wc.WriteString("WHERE ")
wc.WriteString(*processWhereClause)
} else {
if *processWhereClause == "" {
log.Warn("No where-clause specified for process collector. This will generate a very large number of metrics!")
}
@@ -131,7 +129,7 @@ func NewProcessCollector() (Collector, error) {
[]string{"process", "process_id", "creating_process_id"},
nil,
),
queryWhereClause: wc.String(),
queryWhereClause: *processWhereClause,
}, nil
}
@@ -139,12 +137,14 @@ func NewProcessCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *ProcessCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Errorln("[ERROR] failed collecting process metrics:", desc, err)
log.Error("failed collecting process metrics:", desc, err)
return err
}
return nil
}
// Win32_PerfRawData_PerfProc_Process docs:
// - https://msdn.microsoft.com/en-us/library/aa394323(v=vs.85).aspx
type Win32_PerfRawData_PerfProc_Process struct {
Name string
CreatingProcessID uint32
@@ -179,13 +179,24 @@ type Win32_PerfRawData_PerfProc_Process struct {
WorkingSetPrivate uint64
}
type WorkerProcess struct {
AppPoolName string
ProcessId uint32
}
func (c *ProcessCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_PerfProc_Process
q := wmi.CreateQuery(&dst, c.queryWhereClause)
q := queryAllWhere(&dst, c.queryWhereClause)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
var dst_wp []WorkerProcess
q_wp := queryAll(&dst_wp)
if err := wmi.QueryNamespace(q_wp, &dst_wp, "root\\WebAdministration"); err != nil {
log.Debugf("Could not query WebAdministration namespace for IIS worker processes: %v. Skipping", err)
}
for _, process := range dst {
if process.Name == "_Total" {
@@ -196,6 +207,13 @@ func (c *ProcessCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Des
pid := strconv.FormatUint(uint64(process.IDProcess), 10)
cpid := strconv.FormatUint(uint64(process.CreatingProcessID), 10)
for _, wp := range dst_wp {
if wp.ProcessId == process.IDProcess {
processName = strings.Join([]string{processName, wp.AppPoolName}, "_")
break
}
}
ch <- prometheus.MustNewConstMetric(
c.StartTime,
prometheus.GaugeValue,

View File

@@ -1,28 +1,44 @@
// returns data points from Win32_Service
// https://msdn.microsoft.com/en-us/library/aa394418(v=vs.85).aspx - Win32_Service class
// +build windows
package collector
import (
"log"
"strings"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"gopkg.in/alecthomas/kingpin.v2"
)
func init() {
Factories["service"] = NewserviceCollector
}
var (
serviceWhereClause = kingpin.Flag(
"collector.service.services-where",
"WQL 'where' clause to use in WMI metrics query. Limits the response to the services you specify and reduces the size of the response.",
).Default("").String()
)
// A serviceCollector is a Prometheus collector for WMI Win32_Service metrics
type serviceCollector struct {
State *prometheus.Desc
StartMode *prometheus.Desc
Status *prometheus.Desc
queryWhereClause string
}
// NewserviceCollector ...
func NewserviceCollector() (Collector, error) {
const subsystem = "service"
if *serviceWhereClause == "" {
log.Warn("No where-clause specified for service collector. This will generate a very large number of metrics!")
}
return &serviceCollector{
State: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "state"),
@@ -36,6 +52,13 @@ func NewserviceCollector() (Collector, error) {
[]string{"name", "start_mode"},
nil,
),
Status: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "status"),
"The status of the service (Status)",
[]string{"name", "status"},
nil,
),
queryWhereClause: *serviceWhereClause,
}, nil
}
@@ -43,15 +66,18 @@ func NewserviceCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *serviceCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Println("[ERROR] failed collecting service metrics:", desc, err)
log.Error("failed collecting service metrics:", desc, err)
return err
}
return nil
}
// Win32_Service docs:
// - https://msdn.microsoft.com/en-us/library/aa394418(v=vs.85).aspx
type Win32_Service struct {
Name string
State string
Status string
StartMode string
}
@@ -73,11 +99,25 @@ var (
"manual",
"disabled",
}
allStatuses = []string{
"ok",
"error",
"degraded",
"unknown",
"pred fail",
"starting",
"stopping",
"service",
"stressed",
"nonrecover",
"no contact",
"lost comm",
}
)
func (c *serviceCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_Service
q := wmi.CreateQuery(&dst, "")
q := queryAllWhere(&dst, c.queryWhereClause)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
@@ -110,6 +150,20 @@ func (c *serviceCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Des
startMode,
)
}
for _, status := range allStatuses {
isCurrentStatus := 0.0
if status == strings.ToLower(service.Status) {
isCurrentStatus = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.Status,
prometheus.GaugeValue,
isCurrentStatus,
strings.ToLower(service.Name),
status,
)
}
}
return nil, nil
}

View File

@@ -1,13 +1,12 @@
// returns data points from Win32_PerfRawData_PerfOS_System class
// https://web.archive.org/web/20050830140516/http://msdn.microsoft.com/library/en-us/wmisdk/wmi/win32_perfrawdata_perfos_system.asp
// +build windows
package collector
import (
"log"
"errors"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
@@ -31,37 +30,37 @@ func NewSystemCollector() (Collector, error) {
return &SystemCollector{
ContextSwitchesTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "context_switches_total"),
"PerfOS_System.ContextSwitchesPersec",
"Total number of context switches (WMI source: PerfOS_System.ContextSwitchesPersec)",
nil,
nil,
),
ExceptionDispatchesTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "exception_dispatches_total"),
"PerfOS_System.ExceptionDispatchesPersec",
"Total number of exceptions dispatched (WMI source: PerfOS_System.ExceptionDispatchesPersec)",
nil,
nil,
),
ProcessorQueueLength: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "processor_queue_length"),
"PerfOS_System.ProcessorQueueLength",
"Length of processor queue (WMI source: PerfOS_System.ProcessorQueueLength)",
nil,
nil,
),
SystemCallsTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "system_calls_total"),
"PerfOS_System.SystemCallsPersec",
"Total number of system calls (WMI source: PerfOS_System.SystemCallsPersec)",
nil,
nil,
),
SystemUpTime: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "system_up_time"),
"SystemUpTime/Frequency_Object",
"System boot time (WMI source: PerfOS_System.SystemUpTime)",
nil,
nil,
),
Threads: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "threads"),
"PerfOS_System.Threads",
"Current number of threads (WMI source: PerfOS_System.Threads)",
nil,
nil,
),
@@ -72,12 +71,14 @@ func NewSystemCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *SystemCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Println("[ERROR] failed collecting system metrics:", desc, err)
log.Error("failed collecting system metrics:", desc, err)
return err
}
return nil
}
// Win32_PerfRawData_PerfOS_System docs:
// - https://web.archive.org/web/20050830140516/http://msdn.microsoft.com/library/en-us/wmisdk/wmi/win32_perfrawdata_perfos_system.asp
type Win32_PerfRawData_PerfOS_System struct {
ContextSwitchesPersec uint32
ExceptionDispatchesPersec uint32
@@ -91,18 +92,22 @@ type Win32_PerfRawData_PerfOS_System struct {
func (c *SystemCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_PerfOS_System
if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil {
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
if len(dst) == 0 {
return nil, errors.New("WMI query returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.ContextSwitchesTotal,
prometheus.GaugeValue,
prometheus.CounterValue,
float64(dst[0].ContextSwitchesPersec),
)
ch <- prometheus.MustNewConstMetric(
c.ExceptionDispatchesTotal,
prometheus.GaugeValue,
prometheus.CounterValue,
float64(dst[0].ExceptionDispatchesPersec),
)
ch <- prometheus.MustNewConstMetric(
@@ -112,7 +117,7 @@ func (c *SystemCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc
)
ch <- prometheus.MustNewConstMetric(
c.SystemCallsTotal,
prometheus.GaugeValue,
prometheus.CounterValue,
float64(dst[0].SystemCallsPersec),
)
ch <- prometheus.MustNewConstMetric(

174
collector/tcp.go Normal file
View File

@@ -0,0 +1,174 @@
// +build windows
package collector
import (
"errors"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["tcp"] = NewTCPCollector
}
// A TCPCollector is a Prometheus collector for WMI Win32_PerfRawData_Tcpip_TCPv4 metrics
type TCPCollector struct {
ConnectionFailures *prometheus.Desc
ConnectionsActive *prometheus.Desc
ConnectionsEstablished *prometheus.Desc
ConnectionsPassive *prometheus.Desc
ConnectionsReset *prometheus.Desc
SegmentsTotal *prometheus.Desc
SegmentsReceivedTotal *prometheus.Desc
SegmentsRetransmittedTotal *prometheus.Desc
SegmentsSentTotal *prometheus.Desc
}
// NewTCPCollector ...
func NewTCPCollector() (Collector, error) {
const subsystem = "tcp"
return &TCPCollector{
ConnectionFailures: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "connection_failures"),
"(TCP.ConnectionFailures)",
nil,
nil,
),
ConnectionsActive: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "connections_active"),
"(TCP.ConnectionsActive)",
nil,
nil,
),
ConnectionsEstablished: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "connections_established"),
"(TCP.ConnectionsEstablished)",
nil,
nil,
),
ConnectionsPassive: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "connections_passive"),
"(TCP.ConnectionsPassive)",
nil,
nil,
),
ConnectionsReset: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "connections_reset"),
"(TCP.ConnectionsReset)",
nil,
nil,
),
SegmentsTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "segments_total"),
"(TCP.SegmentsTotal)",
nil,
nil,
),
SegmentsReceivedTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "segments_received_total"),
"(TCP.SegmentsReceivedTotal)",
nil,
nil,
),
SegmentsRetransmittedTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "segments_retransmitted_total"),
"(TCP.SegmentsRetransmittedTotal)",
nil,
nil,
),
SegmentsSentTotal: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "segments_sent_total"),
"(TCP.SegmentsSentTotal)",
nil,
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *TCPCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting tcp metrics:", desc, err)
return err
}
return nil
}
// Win32_PerfRawData_Tcpip_TCPv4 docs
// - https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx
type Win32_PerfRawData_Tcpip_TCPv4 struct {
ConnectionFailures uint64
ConnectionsActive uint64
ConnectionsEstablished uint64
ConnectionsPassive uint64
ConnectionsReset uint64
SegmentsPersec uint64
SegmentsReceivedPersec uint64
SegmentsRetransmittedPersec uint64
SegmentsSentPersec uint64
}
func (c *TCPCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_Tcpip_TCPv4
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
if len(dst) == 0 {
return nil, errors.New("WMI query returned empty result set")
}
// Counters
ch <- prometheus.MustNewConstMetric(
c.ConnectionFailures,
prometheus.CounterValue,
float64(dst[0].ConnectionFailures),
)
ch <- prometheus.MustNewConstMetric(
c.ConnectionsActive,
prometheus.CounterValue,
float64(dst[0].ConnectionsActive),
)
ch <- prometheus.MustNewConstMetric(
c.ConnectionsEstablished,
prometheus.CounterValue,
float64(dst[0].ConnectionsEstablished),
)
ch <- prometheus.MustNewConstMetric(
c.ConnectionsPassive,
prometheus.CounterValue,
float64(dst[0].ConnectionsPassive),
)
ch <- prometheus.MustNewConstMetric(
c.ConnectionsReset,
prometheus.CounterValue,
float64(dst[0].ConnectionsReset),
)
ch <- prometheus.MustNewConstMetric(
c.SegmentsTotal,
prometheus.CounterValue,
float64(dst[0].SegmentsPersec),
)
ch <- prometheus.MustNewConstMetric(
c.SegmentsReceivedTotal,
prometheus.CounterValue,
float64(dst[0].SegmentsReceivedPersec),
)
ch <- prometheus.MustNewConstMetric(
c.SegmentsRetransmittedTotal,
prometheus.CounterValue,
float64(dst[0].SegmentsRetransmittedPersec),
)
ch <- prometheus.MustNewConstMetric(
c.SegmentsSentTotal,
prometheus.CounterValue,
float64(dst[0].SegmentsSentPersec),
)
return nil, nil
}

299
collector/textfile.go Normal file
View File

@@ -0,0 +1,299 @@
// Copyright 2015 The Prometheus Authors
// 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.
// +build !notextfile
package collector
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"time"
"github.com/dimchansky/utfbom"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/log"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
var (
textFileDirectory = kingpin.Flag(
"collector.textfile.directory",
"Directory to read text files with metrics from.",
).Default("C:\\Program Files\\wmi_exporter\\textfile_inputs").String()
mtimeDesc = prometheus.NewDesc(
"wmi_textfile_mtime_seconds",
"Unixtime mtime of textfiles successfully read.",
[]string{"file"},
nil,
)
)
type textFileCollector struct {
path string
// Only set for testing to get predictable output.
mtime *float64
}
func init() {
Factories["textfile"] = NewTextFileCollector
}
// NewTextFileCollector returns a new Collector exposing metrics read from files
// in the given textfile directory.
func NewTextFileCollector() (Collector, error) {
return &textFileCollector{
path: *textFileDirectory,
}, nil
}
func convertMetricFamily(metricFamily *dto.MetricFamily, ch chan<- prometheus.Metric) {
var valType prometheus.ValueType
var val float64
allLabelNames := map[string]struct{}{}
for _, metric := range metricFamily.Metric {
labels := metric.GetLabel()
for _, label := range labels {
if _, ok := allLabelNames[label.GetName()]; !ok {
allLabelNames[label.GetName()] = struct{}{}
}
}
}
for _, metric := range metricFamily.Metric {
if metric.TimestampMs != nil {
log.Warnf("Ignoring unsupported custom timestamp on textfile collector metric %v", metric)
}
labels := metric.GetLabel()
var names []string
var values []string
for _, label := range labels {
names = append(names, label.GetName())
values = append(values, label.GetValue())
}
for k := range allLabelNames {
present := false
for _, name := range names {
if k == name {
present = true
break
}
}
if present == false {
names = append(names, k)
values = append(values, "")
}
}
metricType := metricFamily.GetType()
switch metricType {
case dto.MetricType_COUNTER:
valType = prometheus.CounterValue
val = metric.Counter.GetValue()
case dto.MetricType_GAUGE:
valType = prometheus.GaugeValue
val = metric.Gauge.GetValue()
case dto.MetricType_UNTYPED:
valType = prometheus.UntypedValue
val = metric.Untyped.GetValue()
case dto.MetricType_SUMMARY:
quantiles := map[float64]float64{}
for _, q := range metric.Summary.Quantile {
quantiles[q.GetQuantile()] = q.GetValue()
}
ch <- prometheus.MustNewConstSummary(
prometheus.NewDesc(
*metricFamily.Name,
metricFamily.GetHelp(),
names, nil,
),
metric.Summary.GetSampleCount(),
metric.Summary.GetSampleSum(),
quantiles, values...,
)
case dto.MetricType_HISTOGRAM:
buckets := map[float64]uint64{}
for _, b := range metric.Histogram.Bucket {
buckets[b.GetUpperBound()] = b.GetCumulativeCount()
}
ch <- prometheus.MustNewConstHistogram(
prometheus.NewDesc(
*metricFamily.Name,
metricFamily.GetHelp(),
names, nil,
),
metric.Histogram.GetSampleCount(),
metric.Histogram.GetSampleSum(),
buckets, values...,
)
default:
log.Errorf("unknown metric type for file")
continue
}
if metricType == dto.MetricType_GAUGE || metricType == dto.MetricType_COUNTER || metricType == dto.MetricType_UNTYPED {
ch <- prometheus.MustNewConstMetric(
prometheus.NewDesc(
*metricFamily.Name,
metricFamily.GetHelp(),
names, nil,
),
valType, val, values...,
)
}
}
}
func (c *textFileCollector) exportMTimes(mtimes map[string]time.Time, ch chan<- prometheus.Metric) {
// Export the mtimes of the successful files.
if len(mtimes) > 0 {
// Sorting is needed for predictable output comparison in tests.
filenames := make([]string, 0, len(mtimes))
for filename := range mtimes {
filenames = append(filenames, filename)
}
sort.Strings(filenames)
for _, filename := range filenames {
mtime := float64(mtimes[filename].UnixNano() / 1e9)
if c.mtime != nil {
mtime = *c.mtime
}
ch <- prometheus.MustNewConstMetric(mtimeDesc, prometheus.GaugeValue, mtime, filename)
}
}
}
type carriageReturnFilteringReader struct {
r io.Reader
}
// Read returns data from the underlying io.Reader, but with \r filtered out
func (cr carriageReturnFilteringReader) Read(p []byte) (int, error) {
buf := make([]byte, len(p))
n, err := cr.r.Read(buf)
if err != nil && err != io.EOF {
return n, err
}
pi := 0
for i := 0; i < n; i++ {
if buf[i] != '\r' {
p[pi] = buf[i]
pi++
}
}
return pi, err
}
// Update implements the Collector interface.
func (c *textFileCollector) Collect(ch chan<- prometheus.Metric) error {
error := 0.0
mtimes := map[string]time.Time{}
// Iterate over files and accumulate their metrics.
files, err := ioutil.ReadDir(c.path)
if err != nil && c.path != "" {
log.Errorf("Error reading textfile collector directory %q: %s", c.path, err)
error = 1.0
}
fileLoop:
for _, f := range files {
if !strings.HasSuffix(f.Name(), ".prom") {
continue
}
path := filepath.Join(c.path, f.Name())
log.Debugf("Processing file %q", path)
file, err := os.Open(path)
if err != nil {
log.Errorf("Error opening %q: %v", path, err)
error = 1.0
continue
}
var parser expfmt.TextParser
r, encoding := utfbom.Skip(carriageReturnFilteringReader{r: file})
if err = checkBOM(encoding); err != nil {
log.Errorf("Invalid file encoding detected in %s: %s - file must be UTF8", path, err.Error())
error = 1.0
continue
}
parsedFamilies, err := parser.TextToMetricFamilies(r)
closeErr := file.Close()
if closeErr != nil {
log.Warnf("Error closing file: %v", err)
}
if err != nil {
log.Errorf("Error parsing %q: %v", path, err)
error = 1.0
continue
}
for _, mf := range parsedFamilies {
for _, m := range mf.Metric {
if m.TimestampMs != nil {
log.Errorf("Textfile %q contains unsupported client-side timestamps, skipping entire file", path)
error = 1.0
continue fileLoop
}
}
if mf.Help == nil {
help := fmt.Sprintf("Metric read from %s", path)
mf.Help = &help
}
}
// Only set this once it has been parsed and validated, so that
// a failure does not appear fresh.
mtimes[f.Name()] = f.ModTime()
for _, mf := range parsedFamilies {
convertMetricFamily(mf, ch)
}
}
c.exportMTimes(mtimes, ch)
// Export if there were errors.
ch <- prometheus.MustNewConstMetric(
prometheus.NewDesc(
"wmi_textfile_scrape_error",
"1 if there was an error opening or reading a file, 0 otherwise",
nil, nil,
),
prometheus.GaugeValue, error,
)
return nil
}
func checkBOM(encoding utfbom.Encoding) error {
if encoding == utfbom.Unknown || encoding == utfbom.UTF8 {
return nil
}
return fmt.Errorf(encoding.String())
}

View File

@@ -0,0 +1,47 @@
package collector
import (
"github.com/dimchansky/utfbom"
"io/ioutil"
"strings"
"testing"
)
func TestCRFilter(t *testing.T) {
sr := strings.NewReader("line 1\r\nline 2")
cr := carriageReturnFilteringReader{r: sr}
b, err := ioutil.ReadAll(cr)
if err != nil {
t.Error(err)
}
if string(b) != "line 1\nline 2" {
t.Errorf("Unexpected output %q", b)
}
}
func TestCheckBOM(t *testing.T) {
testdata := []struct {
encoding utfbom.Encoding
err string
}{
{utfbom.Unknown, ""},
{utfbom.UTF8, ""},
{utfbom.UTF16BigEndian, "UTF16BigEndian"},
{utfbom.UTF16LittleEndian, "UTF16LittleEndian"},
{utfbom.UTF32BigEndian, "UTF32BigEndian"},
{utfbom.UTF32LittleEndian, "UTF32LittleEndian"},
}
for _, d := range testdata {
err := checkBOM(d.encoding)
if d.err == "" && err != nil {
t.Error(err)
}
if d.err != "" && err == nil {
t.Errorf("Missing expected error %s", d.err)
}
if err != nil && !strings.Contains(err.Error(), d.err) {
t.Error(err)
}
}
}

View File

@@ -1,11 +1,13 @@
// returns data points from Win32_PerfRawData_vmGuestLib_VMem and Win32_PerfRawData_vmGuestLib_VCPU
// +build windows
package collector
import (
"log"
"errors"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
@@ -36,6 +38,7 @@ type VmwareCollector struct {
HostProcessorSpeedMHz *prometheus.Desc
}
// NewVmwareCollector constructs a new VmwareCollector
func NewVmwareCollector() (Collector, error) {
const subsystem = "vmware"
return &VmwareCollector{
@@ -161,11 +164,11 @@ func NewVmwareCollector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *VmwareCollector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collectMem(ch); err != nil {
log.Println("[ERROR] failed collecting vmware memory metrics:", desc, err)
log.Error("failed collecting vmware memory metrics:", desc, err)
return err
}
if desc, err := c.collectCpu(ch); err != nil {
log.Println("[ERROR] failed collecting vmware cpu metrics:", desc, err)
log.Error("failed collecting vmware cpu metrics:", desc, err)
return err
}
return nil
@@ -198,10 +201,13 @@ type Win32_PerfRawData_vmGuestLib_VCPU struct {
func (c *VmwareCollector) collectMem(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_vmGuestLib_VMem
q := wmi.CreateQuery(&dst, "")
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
if len(dst) == 0 {
return nil, errors.New("WMI query returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.MemActive,
@@ -284,10 +290,13 @@ func mbToBytes(mb uint64) float64 {
func (c *VmwareCollector) collectCpu(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_vmGuestLib_VCPU
q := wmi.CreateQuery(&dst, "")
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
if len(dst) == 0 {
return nil, errors.New("WMI query returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.CpuLimitMHz,

View File

@@ -1,6 +1,12 @@
package collector
import "github.com/prometheus/client_golang/prometheus"
import (
"bytes"
"reflect"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
// ...
const (
@@ -18,3 +24,58 @@ type Collector interface {
// Get new metrics and expose them via prometheus registry.
Collect(ch chan<- prometheus.Metric) (err error)
}
func className(src interface{}) string {
s := reflect.Indirect(reflect.ValueOf(src))
t := s.Type()
if s.Kind() == reflect.Slice {
t = t.Elem()
}
return t.Name()
}
func queryAll(src interface{}) string {
var b bytes.Buffer
b.WriteString("SELECT * FROM ")
b.WriteString(className(src))
log.Debugf("Generated WMI query %s", b.String())
return b.String()
}
func queryAllForClass(src interface{}, class string) string {
var b bytes.Buffer
b.WriteString("SELECT * FROM ")
b.WriteString(class)
log.Debugf("Generated WMI query %s", b.String())
return b.String()
}
func queryAllWhere(src interface{}, where string) string {
var b bytes.Buffer
b.WriteString("SELECT * FROM ")
b.WriteString(className(src))
if where != "" {
b.WriteString(" WHERE ")
b.WriteString(where)
}
log.Debugf("Generated WMI query %s", b.String())
return b.String()
}
func queryAllForClassWhere(src interface{}, class string, where string) string {
var b bytes.Buffer
b.WriteString("SELECT * FROM ")
b.WriteString(class)
if where != "" {
b.WriteString(" WHERE ")
b.WriteString(where)
}
log.Debugf("Generated WMI query %s", b.String())
return b.String()
}

115
collector/wmi_test.go Normal file
View File

@@ -0,0 +1,115 @@
package collector
import (
"testing"
)
type fakeWmiClass struct {
Name string
SomeProperty int
}
var (
mapQueryAll = func(src interface{}, class string, where string) string {
return queryAll(src)
}
mapQueryAllWhere = func(src interface{}, class string, where string) string {
return queryAllWhere(src, where)
}
mapQueryAllForClass = func(src interface{}, class string, where string) string {
return queryAllForClass(src, class)
}
mapQueryAllForClassWhere = func(src interface{}, class string, where string) string {
return queryAllForClassWhere(src, class, where)
}
)
type queryFunc func(src interface{}, class string, where string) string
func TestCreateQuery(t *testing.T) {
cases := []struct {
desc string
dst interface{}
class string
where string
queryFunc queryFunc
expected string
}{
{
desc: "queryAll on single instance",
dst: fakeWmiClass{},
queryFunc: mapQueryAll,
expected: "SELECT * FROM fakeWmiClass",
},
{
desc: "queryAll on slice",
dst: []fakeWmiClass{},
queryFunc: mapQueryAll,
expected: "SELECT * FROM fakeWmiClass",
},
{
desc: "queryAllWhere on single instance",
dst: fakeWmiClass{},
where: "foo = bar",
queryFunc: mapQueryAllWhere,
expected: "SELECT * FROM fakeWmiClass WHERE foo = bar",
},
{
desc: "queryAllWhere on slice",
dst: []fakeWmiClass{},
where: "foo = bar",
queryFunc: mapQueryAllWhere,
expected: "SELECT * FROM fakeWmiClass WHERE foo = bar",
},
{
desc: "queryAllWhere on single instance with empty where",
dst: fakeWmiClass{},
queryFunc: mapQueryAllWhere,
expected: "SELECT * FROM fakeWmiClass",
},
{
desc: "queryAllForClass on single instance",
dst: fakeWmiClass{},
class: "someClass",
queryFunc: mapQueryAllForClass,
expected: "SELECT * FROM someClass",
},
{
desc: "queryAllForClass on slice",
dst: []fakeWmiClass{},
class: "someClass",
queryFunc: mapQueryAllForClass,
expected: "SELECT * FROM someClass",
},
{
desc: "queryAllForClassWhere on single instance",
dst: fakeWmiClass{},
class: "someClass",
where: "foo = bar",
queryFunc: mapQueryAllForClassWhere,
expected: "SELECT * FROM someClass WHERE foo = bar",
},
{
desc: "queryAllForClassWhere on slice",
dst: []fakeWmiClass{},
class: "someClass",
where: "foo = bar",
queryFunc: mapQueryAllForClassWhere,
expected: "SELECT * FROM someClass WHERE foo = bar",
},
{
desc: "queryAllForClassWhere on single instance with empty where",
dst: fakeWmiClass{},
class: "someClass",
queryFunc: mapQueryAllForClassWhere,
expected: "SELECT * FROM someClass",
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
if q := c.queryFunc(c.dst, c.class, c.where); q != c.expected {
t.Errorf("Case %q failed: Expected %q, got %q", c.desc, c.expected, q)
}
})
}
}

View File

@@ -0,0 +1,140 @@
{{ template "head" . }}
{{ template "prom_content_head" . }}
<h1>Node Overview - {{ reReplaceAll "(.*?://)([^:/]+?)(:\\d+)?/.*" "$2" .Params.instance }}</h1>
<h3>CPU Usage</h3>
<div id="cpuGraph"></div>
<script>
new PromConsole.Graph({
node: document.querySelector("#cpuGraph"),
expr: "sum by (mode)(irate(wmi_cpu_time_total{job='node',instance='{{ .Params.instance }}',mode!='idle'}[5m]))",
renderer: 'area',
max: {{ with printf "count(count by (cpu)(wmi_cpu_time_total{job='node',instance='%s'}))" .Params.instance | query }}{{ . | first | value }}{{ else}}undefined{{end}},
yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
yTitle: 'Cores'
})
</script>
<h3>Network Utilization</h3>
<div id="networkioGraph"></div>
<script>
new PromConsole.Graph({
node: document.querySelector("#networkioGraph"),
expr: [
"irate(wmi_net_bytes_sent_total{job='node',instance='{{ .Params.instance }}',nic!~'^isatap_ec2_internal'}[5m])",
"irate(wmi_net_bytes_received_total{job='node',instance='{{ .Params.instance }}',nic!~'^isatap_ec2_internal'}[5m])",
],
min: 0,
name: [ 'sent', 'received' ],
yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
yUnits: "B",
yTitle: 'Network IO'
})
</script>
<h3>Disk I/O Utilization</h3>
<div id="diskioGraph"></div>
<script>
new PromConsole.Graph({
node: document.querySelector("#diskioGraph"),
expr: [
"100 - irate(wmi_logical_disk_idle_seconds_total{job='node',instance='{{ .Params.instance }}',volume!~'^HarddiskVolume.*$'}[5m]) * 100",
],
min: 0,
name: '[[ volume ]]',
yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
yUnits: "%",
yTitle: 'Disk I/O Utilization'
})
</script>
<h3>Memory</h3>
<div id="memoryGraph"></div>
<script>
new PromConsole.Graph({
node: document.querySelector("#memoryGraph"),
renderer: 'area',
expr: [
"wmi_cs_physical_memory_bytes{job='node',instance='{{ .Params.instance }}'}",
"wmi_os_physical_memory_free_bytes{job='node',instance='{{ .Params.instance }}'}",
"wmi_cs_physical_memory__bytes{job='node',instance='{{ .Params.instance }}'} - wmi_os_physical_memory_free_bytes{job='node',instance='{{.Params.instance}}'}",
"wmi_os_virtual_memory_bytes{job='node',instance='{{ .Params.instance }}'}",
],
name: ["Physical", "Free", "Used", "Virtual"],
min: 0,
yUnits: "B",
yAxisFormatter: PromConsole.NumberFormatter.humanize1024,
yHoverFormatter: PromConsole.NumberFormatter.humanize1024,
yTitle: 'Memory'
})
</script>
{{ template "prom_right_table_head" }}
<tr><th colspan="2">Overview</th></tr>
<tr>
<td>User CPU</td>
<td>{{ template "prom_query_drilldown" (args (printf "sum(irate(wmi_cpu_time_total{job='node',instance='%s',mode='user'}[5m])) * 100 / count(count by (cpu)(wmi_cpu_time_total{job='node',instance='%s'}))" .Params.instance .Params.instance) "%" "printf.1f") }}</td>
</tr>
<tr>
<td>Privileged CPU</td>
<td>{{ template "prom_query_drilldown" (args (printf "sum(irate(wmi_cpu_time_total{job='node',instance='%s',mode='privileged'}[5m])) * 100 / count(count by (cpu)(wmi_cpu_time_total{job='node',instance='%s'}))" .Params.instance .Params.instance) "%" "printf.1f") }}</td>
</tr>
<tr>
<td>Memory Total</td>
<td>{{ template "prom_query_drilldown" (args (printf "wmi_cs_physical_memory_bytes{job='node',instance='%s'}" .Params.instance) "B" "humanize1024") }}</td>
</tr>
<tr>
<td>Memory Free</td>
<td>{{ template "prom_query_drilldown" (args (printf "wmi_os_physical_memory_free_bytes{job='node',instance='%s'}" .Params.instance) "B" "humanize1024") }}</td>
</tr>
<tr>
<th colspan="2">Network</th>
</tr>
{{ range printf "wmi_net_bytes_received_total{job='node',instance='%s',nic!='isatap_ec2_internal'}" .Params.instance | query | sortByLabel "nic" }}
<tr>
<td>{{ .Labels.nic }} Received</td>
<td>{{ template "prom_query_drilldown" (args (printf "irate(wmi_net_bytes_received_total{job='node',instance='%s',nic='%s'}[5m])" .Labels.instance .Labels.nic) "B/s" "humanize") }}</td>
</tr>
<tr>
<td>{{ .Labels.nic }} Transmitted</td>
<td>{{ template "prom_query_drilldown" (args (printf "irate(wmi_net_bytes_sent_total{job='node',instance='%s',nic='%s'}[5m])" .Labels.instance .Labels.nic) "B/s" "humanize") }}</td>
</tr>
{{ end }}
</tr>
<tr>
<th colspan="2">Disks</th>
</tr>
{{ range printf "wmi_logical_disk_size_bytes{job='node',instance='%s',volume!~'^HarddiskVolume.*$'}" .Params.instance | query | sortByLabel "volume" }}
<tr>
<td>{{ .Labels.volume }} Utilization</td>
<td>{{ template "prom_query_drilldown" (args (printf "100 - irate(wmi_logical_disk_idle_seconds_total{job='node',instance='%s',volume='%s'}[5m]) * 100" .Labels.instance .Labels.volume) "%" "printf.1f") }}</td>
</tr>
{{ end }}
{{ range printf "wmi_logical_disk_size_bytes{job='node',instance='%s',volume!~'^HarddiskVolume.*$'}" .Params.instance | query | sortByLabel "volume" }}
<tr>
<td>{{ .Labels.volume }} Throughput</td>
<td>{{ template "prom_query_drilldown" (args (printf "irate(wmi_logical_disk_read_bytes_total{job='node',instance='%s',volume='%s'}[5m]) + irate(wmi_logical_disk_write_bytes_total{job='node',instance='%s',volume='%s'}[5m])" .Labels.instance .Labels.volume .Labels.instance .Labels.volume) "B/s" "humanize") }}</td>
</tr>
{{ end }}
<tr>
<th colspan="2">Filesystem Fullness</th>
</tr>
{{ define "roughlyNearZero" }}
{{ if gt .1 . }}~0{{ else }}{{ printf "%.1f" . }}{{ end }}
{{ end }}
{{ range printf "wmi_logical_disk_size_bytes{job='node',instance='%s',volume!~'^HarddiskVolume.*$'}" .Params.instance | query | sortByLabel "volume" }}
<tr>
<td>{{ .Labels.volume }}</td>
<td>{{ template "prom_query_drilldown" (args (printf "100 - wmi_logical_disk_free_bytes{job='node',instance='%s',volume='%s'} / wmi_logical_disk_size_bytes{job='node'} * 100" .Labels.instance .Labels.volume) "%" "roughlyNearZero") }}</td>
</tr>
{{ end }}
</tr>
{{ template "prom_right_table_tail" }}
{{ template "prom_content_tail" . }}
{{ template "tail" }}

30
docs/README.md Normal file
View File

@@ -0,0 +1,30 @@
# Documentation
This directory contains documentation of the collectors in the WMI exporter, with information such as what metrics are exported, any flags for additional configuration, and some example usage in alerts and queries.
# Collectors
- [`ad`](collector.ad.md)
- [`cpu`](collector.cpu.md)
- [`cs`](collector.cs.md)
- [`dns`](collector.dns.md)
- [`hyperv`](collector.hyperv.md)
- [`iis`](collector.iis.md)
- [`logical_disk`](collector.logical_disk.md)
- [`memory`](collector.memory.md)
- [`msmq`](collector.msmq.md)
- [`mssql`](collector.mssql.md)
- [`netframework_clrexceptions`](collector.netframework_clrexceptions.md)
- [`netframework_clrinterop`](collector.netframework_clrinterop.md)
- [`netframework_clrjit`](collector.netframework_clrjit.md)
- [`netframework_clrloading`](collector.netframework_clrloading.md)
- [`netframework_clrlocksandthreads`](collector.netframework_clrlocksandthreads.md)
- [`netframework_clrmemory`](collector.netframework_clrmemory.md)
- [`netframework_clrremoting`](collector.netframework_clrremoting.md)
- [`netframework_clrsecurity`](collector.netframework_clrsecurity.md)
- [`net`](collector.net.md)
- [`os`](collector.os.md)
- [`process`](collector.process.md)
- [`service`](collector.service.md)
- [`system`](collector.system.md)
- [`tcp`](collector.tcp.md)
- [`textfile`](collector.textfile.md)
- [`vmware`](collector.vmware.md)

View File

@@ -0,0 +1,47 @@
# %name% collector
The %name% collector exposes metrics about ...
|||
-|-
Metric name prefix | `%name%`
Classes | [`...`](https://msdn.microsoft.com/en-us/library/...)
Enabled by default? | Yes/No
## Flags
### `--collector....`
Add description...
Example: `--collector....`
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_...` | ... | counter/gauge/histogram/summary | ...
### Example metric
...:
`wmi_...{...} 1`
## Useful queries
### Add queries...
`...`
## Alerting examples
### Add alerts...
```yaml
- alert: ""
expr: ""
for: ""
labels:
urgency: ""
annotations:
summary: ""
```

88
docs/collector.ad.md Normal file
View File

@@ -0,0 +1,88 @@
# ad collector
The ad collector exposes metrics about a Active Directory Domain Services domain controller
|||
-|-
Metric name prefix | `ad`
Classes | [`Win32_PerfRawData_DirectoryServices_DirectoryServices`](https://msdn.microsoft.com/en-us/library/ms803980.aspx)
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_ad_address_book_operations_total` | _Not yet documented_ | counter | `operation`
`wmi_ad_address_book_client_sessions` | _Not yet documented_ | gauge | None
`wmi_ad_approximate_highest_distinguished_name_tag` | _Not yet documented_ | gauge | None
`wmi_ad_atq_estimated_delay_seconds` | _Not yet documented_ | gauge | None
`wmi_ad_atq_outstanding_requests` | _Not yet documented_ | gauge | None
`wmi_ad_atq_average_request_latency` | _Not yet documented_ | gauge | None
`wmi_ad_atq_current_threads` | _Not yet documented_ | gauge | `service`
`wmi_ad_searches_total` | _Not yet documented_ | counter | `scope`
`wmi_ad_database_operations_total` | _Not yet documented_ | counter | `operation`
`wmi_ad_binds_total` | _Not yet documented_ | counter | `bind_method`
`wmi_ad_replication_highest_usn` | _Not yet documented_ | counter | `state`
`wmi_ad_replication_data_intrasite_bytes_total` | _Not yet documented_ | counter | `direction`
`wmi_ad_replication_data_intersite_bytes_total` | _Not yet documented_ | counter | `direction`
`wmi_ad_replication_inbound_sync_objects_remaining` | _Not yet documented_ | gauge | None
`wmi_ad_replication_inbound_link_value_updates_remaining` | _Not yet documented_ | gauge | None
`wmi_ad_replication_inbound_objects_updated_total` | _Not yet documented_ | counter | None
`wmi_ad_replication_inbound_objects_filtered_total` | _Not yet documented_ | counter | None
`wmi_ad_replication_inbound_properties_updated_total` | _Not yet documented_ | counter | None
`wmi_ad_replication_inbound_properties_filtered_total` | _Not yet documented_ | counter | None
`wmi_ad_replication_pending_operations` | _Not yet documented_ | gauge | None
`wmi_ad_replication_pending_synchronizations` | _Not yet documented_ | gauge | None
`wmi_ad_replication_sync_requests_total` | _Not yet documented_ | counter | None
`wmi_ad_replication_sync_requests_success_total` | _Not yet documented_ | counter | None
`wmi_ad_replication_sync_requests_schema_mismatch_failure_total` | _Not yet documented_ | counter | None
`wmi_ad_name_translations_total` | _Not yet documented_ | counter | `target_name`
`wmi_ad_change_monitors_registered` | _Not yet documented_ | gauge | None
`wmi_ad_change_monitor_updates_pending` | _Not yet documented_ | gauge | None
`wmi_ad_name_cache_hits_total` | _Not yet documented_ | counter | None
`wmi_ad_name_cache_lookups_total` | _Not yet documented_ | counter | None
`wmi_ad_directory_operations_total` | _Not yet documented_ | counter | `operation`, `origin`
`wmi_ad_directory_search_suboperations_total` | _Not yet documented_ | counter | None
`wmi_ad_security_descriptor_propagation_events_total` | _Not yet documented_ | counter | None
`wmi_ad_security_descriptor_propagation_events_queued` | _Not yet documented_ | gauge | None
`wmi_ad_security_descriptor_propagation_access_wait_total_seconds` | _Not yet documented_ | gauge | None
`wmi_ad_security_descriptor_propagation_items_queued_total` | _Not yet documented_ | counter | None
`wmi_ad_directory_service_threads` | _Not yet documented_ | gauge | None
`wmi_ad_ldap_closed_connections_total` | _Not yet documented_ | counter | None
`wmi_ad_ldap_opened_connections_total` | _Not yet documented_ | counter | `type`
`wmi_ad_ldap_active_threads` | _Not yet documented_ | gauge | None
`wmi_ad_ldap_last_bind_time_seconds` | _Not yet documented_ | gauge | None
`wmi_ad_ldap_searches_total` | _Not yet documented_ | counter | None
`wmi_ad_ldap_udp_operations_total` | _Not yet documented_ | counter | None
`wmi_ad_ldap_writes_total` | _Not yet documented_ | counter | None
`wmi_ad_link_values_cleaned_total` | _Not yet documented_ | counter | None
`wmi_ad_phantom_objects_cleaned_total` | _Not yet documented_ | counter | None
`wmi_ad_phantom_objects_visited_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_group_membership_evaluations_total` | _Not yet documented_ | counter | `group_type`
`wmi_ad_sam_group_membership_global_catalog_evaluations_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_group_membership_evaluations_nontransitive_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_group_membership_evaluations_transitive_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_group_evaluation_latency` | _Not yet documented_ | gauge | `evaluation_type`
`wmi_ad_sam_computer_creation_requests_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_computer_creation_successful_requests_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_user_creation_requests_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_user_creation_successful_requests_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_query_display_requests_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_enumerations_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_membership_changes_total` | _Not yet documented_ | counter | None
`wmi_ad_sam_password_changes_total` | _Not yet documented_ | counter | None
`wmi_ad_tombstoned_objects_collected_total` | _Not yet documented_ | counter | None
`wmi_ad_tombstoned_objects_visited_total` | _Not yet documented_ | counter | None
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

31
docs/collector.cpu.md Normal file
View File

@@ -0,0 +1,31 @@
# cpu collector
The cpu collector exposes metrics about CPU usage
|||
-|-
Metric name prefix | `cpu`
Classes | [`Win32_PerfRawData_PerfOS_Processor`](https://msdn.microsoft.com/en-us/library/aa394317(v=vs.90).aspx)
Enabled by default? | Yes
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_cpu_cstate_seconds_total` | Time spent in low-power idle states | counter | `core`, `state`
`wmi_cpu_time_total` | Time that processor spent in different modes (idle, user, system, ...) | counter | `core`, `mode`
`wmi_cpu_interrupts_total` | Total number of received and serviced hardware interrupts | counter | `core`
`wmi_cpu_dpcs_total` | Total number of received and serviced deferred procedure calls (DPCs) | counter | `core`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

29
docs/collector.cs.md Normal file
View File

@@ -0,0 +1,29 @@
# cs collector
The cs collector exposes metrics detailing the hardware of the computer system
|||
-|-
Metric name prefix | `cs`
Classes | [`Win32_ComputerSystem`](https://msdn.microsoft.com/en-us/library/aa394102)
Enabled by default? | Yes
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_cs_logical_processors` | Number of installed logical processors | gauge | None
`wmi_cs_physical_memory_bytes` | Total installed physical memory | gauge | None
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

49
docs/collector.dns.md Normal file
View File

@@ -0,0 +1,49 @@
# dns collector
The dns collector exposes metrics about the DNS server
|||
-|-
Metric name prefix | `dns`
Classes | [`Win32_PerfRawData_DNS_DNS`](https://technet.microsoft.com/en-us/library/cc977686.aspx)
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_dns_zone_transfer_requests_received_total` | _Not yet documented_ | counter | `qtype`
`wmi_dns_zone_transfer_requests_sent_total` | _Not yet documented_ | counter | `qtype`
`wmi_dns_zone_transfer_response_received_total` | _Not yet documented_ | counter | `qtype`
`wmi_dns_zone_transfer_success_received_total` | _Not yet documented_ | counter | `qtype`, `protocol`
`wmi_dns_zone_transfer_success_sent_total` | _Not yet documented_ | counter | `qtype`
`wmi_dns_zone_transfer_failures_total` | _Not yet documented_ | counter | None
`wmi_dns_memory_used_bytes_total` | _Not yet documented_ | gauge | `area`
`wmi_dns_dynamic_updates_queued` | _Not yet documented_ | gauge | None
`wmi_dns_dynamic_updates_received_total` | _Not yet documented_ | counter | `operation`
`wmi_dns_dynamic_updates_failures_total` | _Not yet documented_ | counter | `reason`
`wmi_dns_notify_received_total` | _Not yet documented_ | counter | None
`wmi_dns_notify_sent_total` | _Not yet documented_ | counter | None
`wmi_dns_secure_update_failures_total` | _Not yet documented_ | counter | None
`wmi_dns_secure_update_received_total` | _Not yet documented_ | counter | None
`wmi_dns_queries_total` | _Not yet documented_ | counter | `protocol`
`wmi_dns_responses_total` | _Not yet documented_ | counter | `protocol`
`wmi_dns_recursive_queries_total` | _Not yet documented_ | counter | None
`wmi_dns_recursive_query_failures_total` | _Not yet documented_ | counter | None
`wmi_dns_recursive_query_send_timeouts_total` | _Not yet documented_ | counter | None
`wmi_dns_wins_queries_total` | _Not yet documented_ | counter | `direction`
`wmi_dns_wins_responses_total` | _Not yet documented_ | counter | `direction`
`wmi_dns_unmatched_responses_total` | _Not yet documented_ | counter | None
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

102
docs/collector.hyperv.md Normal file
View File

@@ -0,0 +1,102 @@
# hyperv collector
The hyperv collector exposes metrics about the Hyper-V hypervisor
|||
-|-
Metric name prefix | `hyperv`
Classes | `Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary`<br/>`Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition`<br/>`Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition`<br/>`Win32_PerfRawData_HvStats_HyperVHypervisor`<br/>`Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor`<br/>`Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor`<br/>`Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch`<br/>`Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter`<br/>`Win32_PerfRawData_Counters_HyperVVirtualStorageDevice`<br/>`Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_hyper_health_critical` | _Not yet documented_ | counter | None
`wmi_hyper_health_ok` | _Not yet documented_ | counter | None
`wmi_hyper_vid_physical_pages_allocated` | _Not yet documented_ | counter | `vm`
`wmi_hyper_vid_preferred_numa_node_index` | _Not yet documented_ | counter | `vm`
`wmi_hyper_vid_remote_physical_pages` | _Not yet documented_ | counter | `vm`
`wmi_hyper_root_partition_address_spaces` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_attached_devices` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_deposited_pages` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_device_dma_errors` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_device_interrupt_errors` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_device_interrupt_mappings` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_device_interrupt_throttle_events` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_preferred_numa_node_index` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_gpa_space_modifications` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_io_tlb_flush_cost` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_io_tlb_flush` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_recommended_virtual_tlb_size` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_physical_pages_allocated` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_1G_device_pages` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_1G_gpa_pages` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_2M_device_pages` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_2M_gpa_pages` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_4K_device_pages` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_4K_gpa_pages` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_virtual_tlb_flush_entires` | _Not yet documented_ | counter | None
`wmi_hyper_root_partition_virtual_tlb_pages` | _Not yet documented_ | counter | None
`wmi_hyper_hypervisor_virtual_processors` | _Not yet documented_ | counter | None
`wmi_hyper_hypervisor_logical_processors` | _Not yet documented_ | counter | None
`wmi_hyper_host_cpu_guest_run_time` | _Not yet documented_ | counter | `core`
`wmi_hyper_host_cpu_hypervisor_run_time` | _Not yet documented_ | counter | `core`
`wmi_hyper_host_cpu_remote_run_time` | _Not yet documented_ | counter | `core`
`wmi_hyper_host_cpu_total_run_time` | _Not yet documented_ | counter | `core`
`wmi_hyper_vm_cpu_guest_run_time` | _Not yet documented_ | counter | `vm`, `core`
`wmi_hyper_vm_cpu_hypervisor_run_time` | _Not yet documented_ | counter | `vm`, `core`
`wmi_hyper_vm_cpu_remote_run_time` | _Not yet documented_ | counter | `vm`, `core`
`wmi_hyper_vm_cpu_total_run_time` | _Not yet documented_ | counter | `vm`, `core`
`wmi_hyper_vswitch_broadcast_packets_received_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_broadcast_packets_sent_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_bytes_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_bytes_received_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_bytes_sent_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_directed_packets_received_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_directed_packets_send_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_dropped_packets_incoming_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_dropped_packets_outcoming_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_extensions_dropped_packets_incoming_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_extensions_dropped_packets_outcoming_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_learned_mac_addresses_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_multicast_packets_received_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_multicast_packets_sent_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_number_of_send_channel_moves_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_number_of_vmq_moves_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_packets_flooded_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_packets_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_packets_received_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_packets_sent_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_vswitch_purged_mac_addresses_total` | _Not yet documented_ | counter | `vswitch`
`wmi_hyper_ethernet_bytes_dropped` | _Not yet documented_ | counter | `adapter`
`wmi_hyper_ethernet_bytes_received` | _Not yet documented_ | counter | `adapter`
`wmi_hyper_ethernet_bytes_sent` | _Not yet documented_ | counter | `adapter`
`wmi_hyper_ethernet_frames_dropped` | _Not yet documented_ | counter | `adapter`
`wmi_hyper_ethernet_frames_received` | _Not yet documented_ | counter | `adapter`
`wmi_hyper_ethernet_frames_sent` | _Not yet documented_ | counter | `adapter`
`wmi_hyper_vm_device_error_count` | _Not yet documented_ | counter | `vm_device`
`wmi_hyper_vm_device_queue_length` | _Not yet documented_ | counter | `vm_device`
`wmi_hyper_vm_device_bytes_read` | _Not yet documented_ | counter | `vm_device`
`wmi_hyper_vm_device_operations_read` | _Not yet documented_ | counter | `vm_device`
`wmi_hyper_vm_device_bytes_written` | _Not yet documented_ | counter | `vm_device`
`wmi_hyper_vm_device_operations_written` | _Not yet documented_ | counter | `vm_device`
`wmi_hyper_vm_interface_bytes_received` | _Not yet documented_ | counter | `vm_interface`
`wmi_hyper_vm_interface_bytes_sent` | _Not yet documented_ | counter | `vm_interface`
`wmi_hyper_vm_interface_packets_incoming_dropped` | _Not yet documented_ | counter | `vm_interface`
`wmi_hyper_vm_interface_packets_outgoing_dropped` | _Not yet documented_ | counter | `vm_interface`
`wmi_hyper_vm_interface_packets_received` | _Not yet documented_ | counter | `vm_interface`
`wmi_hyper_vm_interface_packets_sent` | _Not yet documented_ | counter | `vm_interface`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

140
docs/collector.iis.md Normal file
View File

@@ -0,0 +1,140 @@
# iis collector
The iis collector exposes metrics about the IIS server
|||
-|-
Metric name prefix | `iis`
Classes | `Win32_PerfRawData_W3SVC_WebService`<br/>`Win32_PerfRawData_APPPOOLCountersProvider_APPPOOLWAS`<br/>`Win32_PerfRawData_W3SVCW3WPCounterProvider_W3SVCW3WP`<br/>`Win32_PerfRawData_W3SVC_WebServiceCache`
Enabled by default? | No
## Flags
### `--collector.iis.site-whitelist`
If given, a site needs to match the whitelist regexp in order for the corresponding metrics to be reported.
### `--collector.iis.site-blacklist`
If given, a site needs to *not* match the blacklist regexp in order for the corresponding metrics to be reported.
### `--collector.iis.app-whitelist`
If given, an application needs to match the whitelist regexp in order for the corresponding metrics to be reported.
### `--collector.iis.app-blacklist`
If given, an application needs to *not* match the blacklist regexp in order for the corresponding metrics to be reported.
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_iis_current_anonymous_users` | _Not yet documented_ | counter | `site`
`wmi_iis_current_blocked_async_io_requests` | _Not yet documented_ | counter | `site`
`wmi_iis_current_cgi_requests` | _Not yet documented_ | counter | `site`
`wmi_iis_current_connections` | _Not yet documented_ | counter | `site`
`wmi_iis_current_isapi_extension_requests` | _Not yet documented_ | counter | `site`
`wmi_iis_current_non_anonymous_users` | _Not yet documented_ | counter | `site`
`wmi_iis_received_bytes_total` | _Not yet documented_ | counter | `site`
`wmi_iis_sent_bytes_total` | _Not yet documented_ | counter | `site`
`wmi_iis_anonymous_users_total` | _Not yet documented_ | counter | `site`
`wmi_iis_blocked_async_io_requests_total` | _Not yet documented_ | counter | `site`
`wmi_iis_cgi_requests_total` | _Not yet documented_ | counter | `site`
`wmi_iis_connection_attempts_all_instances_total` | _Not yet documented_ | counter | `site`
`wmi_iis_requests_total` | _Not yet documented_ | counter | `site`, `method`
`wmi_iis_files_received_total` | _Not yet documented_ | counter | `site`
`wmi_iis_files_sent_total` | _Not yet documented_ | counter | `site`
`wmi_iis_ipapi_extension_requests_total` | _Not yet documented_ | counter | `site`
`wmi_iis_locked_errors_total` | _Not yet documented_ | counter | `site`
`wmi_iis_logon_attempts_total` | _Not yet documented_ | counter | `site`
`wmi_iis_non_anonymous_users_total` | _Not yet documented_ | counter | `site`
`wmi_iis_not_found_errors_total` | _Not yet documented_ | counter | `site`
`wmi_iis_rejected_async_io_requests_total` | _Not yet documented_ | counter | `site`
`wmi_iis_current_application_pool_state` | _Not yet documented_ | counter | `app`, `state`
`wmi_iis_current_application_pool_start_time` | _Not yet documented_ | counter | `app`
`wmi_iis_current_worker_processes` | _Not yet documented_ | counter | `app`
`wmi_iis_maximum_worker_processes` | _Not yet documented_ | counter | `app`
`wmi_iis_recent_worker_process_failures` | _Not yet documented_ | counter | `app`
`wmi_iis_time_since_last_worker_process_failure` | _Not yet documented_ | counter | `app`
`wmi_iis_total_application_pool_recycles` | _Not yet documented_ | counter | `app`
`wmi_iis_total_application_pool_start_time` | _Not yet documented_ | counter | `app`
`wmi_iis_total_worker_processes_created` | _Not yet documented_ | counter | `app`
`wmi_iis_total_worker_process_failures` | _Not yet documented_ | counter | `app`
`wmi_iis_total_worker_process_ping_failures` | _Not yet documented_ | counter | `app`
`wmi_iis_total_worker_process_shutdown_failures` | _Not yet documented_ | counter | `app`
`wmi_iis_total_worker_process_startup_failures` | _Not yet documented_ | counter | `app`
`wmi_iis_worker_cache_active_flushed_entries` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_file_cache_memory_bytes` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_file_cache_max_memory_bytes` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_file_cache_flushes_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_file_cache_queries_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_file_cache_hits_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_file_cache_items` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_file_cache_items_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_file_cache_items_flushed_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_uri_cache_flushes_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_uri_cache_queries_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_uri_cache_hits_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_uri_cache_items` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_uri_cache_items_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_uri_cache_items_flushed_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_metadata_cache_items` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_metadata_cache_flushes_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_metadata_cache_queries_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_metadata_cache_hits_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_metadata_cache_items_cached_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_metadata_cache_items_flushed_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_output_cache_active_flushed_items` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_output_cache_items` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_output_cache_memory_bytes` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_output_queries_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_output_cache_hits_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_output_cache_items_flushed_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_output_cache_flushes_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_threads` | _Not yet documented_ | counter | `app`, `pid`, `state`
`wmi_iis_worker_max_threads` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_requests_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_current_requests` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_request_errors_total` | _Not yet documented_ | counter | `app`, `pid`, `status_code`
`wmi_iis_worker_current_websocket_requests` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_websocket_connection_attempts_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_websocket_connection_accepted_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_worker_websocket_connection_rejected_total` | _Not yet documented_ | counter | `app`, `pid`
`wmi_iis_server_cache_active_flushed_entries` | _Not yet documented_ | counter | None
`wmi_iis_server_file_cache_memory_bytes` | _Not yet documented_ | counter | None
`wmi_iis_server_file_cache_max_memory_bytes` | _Not yet documented_ | counter | None
`wmi_iis_server_file_cache_flushes_total` | _Not yet documented_ | counter | None
`wmi_iis_server_file_cache_queries_total` | _Not yet documented_ | counter | None
`wmi_iis_server_file_cache_hits_total` | _Not yet documented_ | counter | None
`wmi_iis_server_file_cache_items` | _Not yet documented_ | counter | None
`wmi_iis_server_file_cache_items_total` | _Not yet documented_ | counter | None
`wmi_iis_server_file_cache_items_flushed_total` | _Not yet documented_ | counter | None
`wmi_iis_server_uri_cache_flushes_total` | _Not yet documented_ | counter | `mode`
`wmi_iis_server_uri_cache_queries_total` | _Not yet documented_ | counter | `mode`
`wmi_iis_server_uri_cache_hits_total` | _Not yet documented_ | counter | `mode`
`wmi_iis_server_uri_cache_items` | _Not yet documented_ | counter | `mode`
`wmi_iis_server_uri_cache_items_total` | _Not yet documented_ | counter | `mode`
`wmi_iis_server_uri_cache_items_flushed_total` | _Not yet documented_ | counter | `mode`
`wmi_iis_server_metadata_cache_items` | _Not yet documented_ | counter | None
`wmi_iis_server_metadata_cache_flushes_total` | _Not yet documented_ | counter | None
`wmi_iis_server_metadata_cache_queries_total` | _Not yet documented_ | counter | None
`wmi_iis_server_metadata_cache_hits_total` | _Not yet documented_ | counter | None
`wmi_iis_server_metadata_cache_items_cached_total` | _Not yet documented_ | counter | None
`wmi_iis_server_metadata_cache_items_flushed_total` | _Not yet documented_ | counter | None
`wmi_iis_server_output_cache_active_flushed_items` | _Not yet documented_ | counter | None
`wmi_iis_server_output_cache_items` | _Not yet documented_ | counter | None
`wmi_iis_server_output_cache_memory_bytes` | _Not yet documented_ | counter | None
`wmi_iis_server_output_cache_queries_total` | _Not yet documented_ | counter | None
`wmi_iis_server_output_cache_hits_total` | _Not yet documented_ | counter | None
`wmi_iis_server_output_cache_items_flushed_total` | _Not yet documented_ | counter | None
`wmi_iis_server_output_cache_flushes_total` | _Not yet documented_ | counter | None
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,44 @@
# logical_disk collector
The logical_disk collector exposes metrics about logical disks (in contrast to physical disks)
|||
-|-
Metric name prefix | `logical_disk`
Classes | [`Win32_PerfRawData_PerfDisk_LogicalDisk`](https://msdn.microsoft.com/en-us/windows/hardware/aa394307(v=vs.71))
Enabled by default? | Yes
## Flags
### `--collector.logical_disk.volume-whitelist`
If given, a disk needs to match the whitelist regexp in order for the corresponding disk metrics to be reported
### `--collector.logical_disk.volume-blacklist`
If given, a disk needs to *not* match the blacklist regexp in order for the corresponding disk metrics to be reported
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`requests_queued` | _Not yet documented_ | gauge | `volume`
`read_bytes_total` | _Not yet documented_ | counter | `volume`
`reads_total` | _Not yet documented_ | counter | `volume`
`write_bytes_total` | _Not yet documented_ | counter | `volume`
`writes_total` | _Not yet documented_ | counter | `volume`
`read_seconds_total` | _Not yet documented_ | counter | `volume`
`write_seconds_total` | _Not yet documented_ | counter | `volume`
`free_bytes` | _Not yet documented_ | gauge | `volume`
`size_bytes` | _Not yet documented_ | gauge | `volume`
`idle_seconds_total` | _Not yet documented_ | counter | `volume`
`split_ios_total` | _Not yet documented_ | counter | `volume`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

59
docs/collector.memory.md Normal file
View File

@@ -0,0 +1,59 @@
# memory collector
The memory collector exposes metrics about system memory usage
Metric name prefix | `memory`
Classes | `Win32_PerfRawData_PerfOS_Memory`
Enabled by default? | Yes
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_cs_logical_processors` | Number of installed logical processors | gauge | None
`wmi_cs_physical_memory_bytes` | Total installed physical memory | gauge | None
`wmi_memory_available_bytes` | The amount of physical memory immediately available for allocation to a process or for system use. It is equal to the sum of memory assigned to the standby (cached), free and zero page lists | gauge | None
`wmi_memory_cache_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_cache_bytes_peak` | _Not yet documented_ | gauge | None
`wmi_memory_cache_faults_total` | _Not yet documented_ | gauge | None
`wmi_memory_commit_limit` | _Not yet documented_ | gauge | None
`wmi_memory_committed_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_demand_zero_faults_total` | The number of zeroed pages required to satisfy faults. Zeroed pages, pages emptied of previously stored data and filled with zeros, are a security feature of Windows that prevent processes from seeing data stored by earlier processes that used the memory space | gauge | None
`wmi_memory_free_and_zero_page_list_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_free_system_page_table_entries` | _Not yet documented_ | gauge | None
`wmi_memory_modified_page_list_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_page_faults_total` | _Not yet documented_ | gauge | None
`wmi_memory_swap_page_reads_total` | Number of disk page reads (a single read operation reading several pages is still only counted once) | gauge | None
`wmi_memory_swap_pages_read_total` | Number of pages read across all page reads (ie counting all pages read even if they are read in a single operation) | gauge | None
`wmi_memory_swap_pages_written_total` | Number of pages written across all page writes (ie counting all pages written even if they are written in a single operation) | gauge | None
`wmi_memory_swap_page_operations_total` | Total number of swap page read and writes (PagesPersec) | gauge | None
`wmi_memory_swap_page_writes_total` | Number of disk page writes (a single write operation writing several pages is still only counted once) | gauge | None
`wmi_memory_pool_nonpaged_allocs_total` | The number of calls to allocate space in the nonpaged pool. The nonpaged pool is an area of system memory area for objects that cannot be written to disk, and must remain in physical memory as long as they are allocated | gauge | None
`wmi_memory_pool_nonpaged_bytes_total` | _Not yet documented_ | gauge | None
`wmi_memory_pool_paged_allocs_total` | _Not yet documented_ | gauge | None
`wmi_memory_pool_paged_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_pool_paged_resident_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_standby_cache_core_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_standby_cache_normal_priority_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_standby_cache_reserve_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_system_cache_resident_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_system_code_resident_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_system_code_total_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_system_driver_resident_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_system_driver_total_bytes` | _Not yet documented_ | gauge | None
`wmi_memory_transition_faults_total` | _Not yet documented_ | gauge | None
`wmi_memory_transition_pages_repurposed_total` | _Not yet documented_ | gauge | None
`wmi_memory_write_copies_total` | The number of page faults caused by attempting to write that were satisfied by copying the page from elsewhere in physical memory | gauge | None
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

33
docs/collector.msmq.md Normal file
View File

@@ -0,0 +1,33 @@
# msmq collector
The msmq collector exposes metrics about the queues on a MSMQ server
|||
-|-
Metric name prefix | `msmq`
Classes | `Win32_PerfRawData_MSMQ_MSMQQueue`
Enabled by default? | No
## Flags
### `--collector.msmq.msmq-where`
A WMI filter on which queues to include. `%` is a wildcard, and can be used to match on substrings.
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_msmq_bytes_in_journal_queue` | Size of queue journal in bytes | gauge | `name`
`wmi_msmq_bytes_in_queue` | Size of queue in bytes | gauge | `name`
`wmi_msmq_messages_in_journal_queue` | Count messages in queue journal | gauge | `name`
`wmi_msmq_messages_in_queue` | Count messages in queue | gauge | `name`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

241
docs/collector.mssql.md Normal file
View File

@@ -0,0 +1,241 @@
# mssql collector
The mssql collector exposes metrics about the MSSQL server
|||
-|-
Metric name prefix | `mssql`
Classes | [`Win32_PerfRawData_MSSQLSERVER_SQLServerAccessMethods`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-access-methods-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerAvailabilityReplica`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-availability-replica)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerBufferManager`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-buffer-manager-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerDatabaseReplica`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-database-replica)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerDatabases`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-databases-object?view=sql-server-2017)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerGeneralStatistics`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-general-statistics-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerLocks`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-locks-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerMemoryManager`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-memory-manager-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerSQLStatistics`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-sql-statistics-object)
Enabled by default? | No
## Flags
### `--collectors.mssql.classes-enabled`
Comma-separated list of MSSQL WMI classes to use. Supported values are `accessmethods`, `availreplica`, `bufman`, `databases`, `dbreplica`, `genstats`, `locks`, `memmgr` and `sqlstats`.
### `--collectors.mssql.class-print`
If true, print available mssql WMI classes and exit. Only displays if the mssql collector is enabled.
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_mssql_collector_duration_seconds` | The time taken for each sub-collector to return | counter | `collector`, `instance`
`wmi_mssql_collector_success` | 1 if sub-collector succeeded, 0 otherwise | counter | `collector`, `instance`
`wmi_mssql_accessmethods_au_batch_cleanups` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_au_cleanups` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_by_reference_lob_creates` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_by_reference_lob_uses` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_lob_read_aheads` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_column_value_pulls` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_column_value_pushes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_deferred_dropped_aus` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_deferred_dropped_rowsets` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_dropped_rowset_cleanups` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_dropped_rowset_skips` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_extent_deallocations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_extent_allocations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_au_batch_cleanup_failures` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_leaf_page_cookie_failures` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_tree_page_cookie_failures` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_forwarded_records` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_free_space_page_fetches` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_free_space_scans` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_full_scans` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_index_searches` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_insysxact_waits` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_lob_handle_creates` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_lob_handle_destroys` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_lob_ss_provider_creates` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_lob_ss_provider_destroys` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_lob_ss_provider_truncations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_mixed_page_allocations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_page_compression_attempts` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_page_deallocations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_page_allocations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_page_compressions` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_page_splits` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_probe_scans` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_range_scans` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_scan_point_revalidations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_ghost_record_skips` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_table_lock_escalations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_leaf_page_cookie_uses` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_tree_page_cookie_uses` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_workfile_creates` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_worktables_creates` | _Not yet documented_ | counter | `instance`
`wmi_mssql_accessmethods_worktables_from_cache_ratio` | _Not yet documented_ | counter | `instance`
`wmi_mssql_availreplica_received_from_replica_bytes` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_availreplica_sent_to_replica_bytes` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_availreplica_sent_to_transport_bytes` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_availreplica_initiated_flow_controls` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_availreplica_flow_control_wait_seconds` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_availreplica_receives_from_replica` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_availreplica_resent_messages` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_availreplica_sends_to_replica` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_availreplica_sends_to_transport` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_bufman_background_writer_pages` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_buffer_cache_hit_ratio` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_checkpoint_pages` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_database_pages` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_extension_allocated_pages` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_extension_free_pages` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_extension_in_use_as_percentage` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_extension_outstanding_io` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_extension_page_evictions` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_extension_page_reads` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_extension_page_unreferenced_seconds` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_extension_page_writes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_free_list_stalls` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_integral_controller_slope` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_lazywrites` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_page_life_expectancy_seconds` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_page_lookups` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_page_reads` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_page_writes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_read_ahead_pages` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_read_ahead_issuing_seconds` | _Not yet documented_ | counter | `instance`
`wmi_mssql_bufman_target_pages` | _Not yet documented_ | counter | `instance`
`wmi_mssql_dbreplica_database_flow_control_wait_seconds` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_database_initiated_flow_controls` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_received_file_bytes` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_group_commits` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_group_commit_stall_seconds` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_apply_pending_queue` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_apply_ready_queue` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_compressed_bytes` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_decompressed_bytes` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_received_bytes` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_compression_cachehits` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_compression_cachemisses` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_compressions` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_decompressions` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_remaining_for_undo` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_log_send_queue` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_mirrored_write_transactions` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_recovery_queue_records` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_redo_blocks` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_redo_remaining_bytes` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_redone_bytes` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_redones` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_total_log_requiring_undo` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_dbreplica_transaction_delay_seconds` | _Not yet documented_ | counter | `instance`, `replica`
`wmi_mssql_databases_active_transactions` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_backup_restore_operations` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_bulk_copy_rows` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_bulk_copy_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_commit_table_entries` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_data_files_size_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_dbcc_logical_scan_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_group_commit_stall_seconds` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_flushed_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_cache_hit_ratio` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_cache_reads` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_files_size_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_files_used_size_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_flushes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_flush_waits` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_flush_wait_seconds` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_flush_write_seconds` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_growths` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_cache_misses` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_disk_reads` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_hash_deletes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_hash_inserts` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_invalid_hash_entries` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_log_scan_pushes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_log_writer_pushes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_empty_free_pool_pushes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_low_memory_pushes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_no_free_buffer_pushes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_req_behind_trunc` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_requests_old_vlf` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_requests` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_total_active_log_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_pool_total_shared_pool_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_shrinks` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_truncations` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_log_used_percent` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_pending_repl_transactions` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_repl_transactions` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_shrink_data_movement_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_tracked_transactions` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_transactions` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_write_transactions` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_xtp_controller_dlc_fetch_latency_seconds` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_xtp_controller_dlc_peak_latency_seconds` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_xtp_controller_log_processed_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_databases_xtp_memory_used_bytes` | _Not yet documented_ | counter | `instance`, `database`
`wmi_mssql_genstats_active_temp_tables` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_connection_resets` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_event_notifications_delayed_drop` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_http_authenticated_requests` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_logical_connections` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_logins` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_logouts` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_mars_deadlocks` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_non_atomic_yields` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_blocked_processes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_soap_empty_requests` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_soap_method_invocations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_soap_session_initiate_requests` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_soap_session_terminate_requests` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_soapsql_requests` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_soapwsdl_requests` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_sql_trace_io_provider_lock_waits` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_tempdb_recovery_unit_ids_generated` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_tempdb_rowset_ids_generated` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_temp_tables_creations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_temp_tables_awaiting_destruction` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_trace_event_notification_queue_size` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_transactions` | _Not yet documented_ | counter | `instance`
`wmi_mssql_genstats_user_connections` | _Not yet documented_ | counter | `instance`
`wmi_mssql_locks_average_wait_seconds` | _Not yet documented_ | counter | `instance`, `resource`
`wmi_mssql_locks_lock_requests` | _Not yet documented_ | counter | `instance`, `resource`
`wmi_mssql_locks_lock_timeouts` | _Not yet documented_ | counter | `instance`, `resource`
`wmi_mssql_locks_lock_timeouts_excluding_NOWAIT` | _Not yet documented_ | counter | `instance`, `resource`
`wmi_mssql_locks_lock_waits` | _Not yet documented_ | counter | `instance`, `resource`
`wmi_mssql_locks_lock_wait_seconds` | _Not yet documented_ | counter | `instance`, `resource`
`wmi_mssql_locks_deadlocks` | _Not yet documented_ | counter | `instance`, `resource`
`wmi_mssql_memmgr_connection_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_database_cache_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_external_benefit_of_memory` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_free_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_granted_workspace_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_lock_blocks` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_allocated_lock_blocks` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_lock_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_lock_owner_blocks` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_allocated_lock_owner_blocks` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_log_pool_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_maximum_workspace_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_outstanding_memory_grants` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_pending_memory_grants` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_optimizer_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_reserved_server_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_sql_cache_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_stolen_server_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_target_server_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_memmgr_total_server_memory_bytes` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_auto_parameterization_attempts` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_batch_requests` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_failed_auto_parameterization_attempts` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_forced_parameterizations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_guided_plan_executions` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_misguided_plan_executions` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_safe_auto_parameterization_attempts` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_sql_attentions` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_sql_compilations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_sql_recompilations` | _Not yet documented_ | counter | `instance`
`wmi_mssql_sqlstats_unsafe_auto_parameterization_attempts` | _Not yet documented_ | counter | `instance`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

45
docs/collector.net.md Normal file
View File

@@ -0,0 +1,45 @@
# net collector
The net collector exposes metrics about network interfaces
|||
-|-
Metric name prefix | `net`
Classes | [`Win32_PerfRawData_Tcpip_NetworkInterface`](https://technet.microsoft.com/en-us/security/aa394340(v=vs.80))
Enabled by default? | Yes
## Flags
### `--collector.net.nic-whitelist`
If given, an interface name needs to match the whitelist regexp in order for the corresponding metrics to be reported
### `--collector.net.nic-blacklist`
If given, an interface name needs to *not* match the blacklist regexp in order for the corresponding metrics to be reported
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_net_bytes_received_total` | _Not yet documented_ | counter | `nic`
`wmi_net_bytes_sent_total` | _Not yet documented_ | counter | `nic`
`wmi_net_bytes_total` | _Not yet documented_ | counter | `nic`
`wmi_net_packets_outbound_discarded` | _Not yet documented_ | counter | `nic`
`wmi_net_packets_outbound_errors` | _Not yet documented_ | counter | `nic`
`wmi_net_packets_received_discarded` | _Not yet documented_ | counter | `nic`
`wmi_net_packets_received_errors` | _Not yet documented_ | counter | `nic`
`wmi_net_packets_received_total` | _Not yet documented_ | counter | `nic`
`wmi_net_packets_received_unknown` | _Not yet documented_ | counter | `nic`
`wmi_net_packets_total` | _Not yet documented_ | counter | `nic`
`wmi_net_packets_sent_total` | _Not yet documented_ | counter | `nic`
`wmi_net_current_bandwidth` | _Not yet documented_ | counter | `nic`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,32 @@
# netframework_clrexceptions collector
The netframework_clrexceptions collector exposes metrics about CLR exceptions in the dotnet framework.
|||
-|-
Metric name prefix | `netframework_clrexceptions`
Classes | `Win32_PerfRawData_NETFramework_NETCLRExceptions`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_...` | ... | counter/gauge/histogram/summary | ...
`wmi_netframework_clrexceptions_exceptions_thrown_total` | Displays the total number of exceptions thrown since the application started. This includes both .NET exceptions and unmanaged exceptions that are converted into .NET exceptions. | counter | `process`
`wmi_netframework_clrexceptions_exceptions_filters_total` | Displays the total number of .NET exception filters executed. An exception filter evaluates regardless of whether an exception is handled. | counter | `process`
`wmi_netframework_clrexceptions_exceptions_finallys_total` | Displays the total number of finally blocks executed. Only the finally blocks executed for an exception are counted; finally blocks on normal code paths are not counted by this counter. | counter | `process`
`wmi_netframework_clrexceptions_throw_to_catch_depth_total` | Displays the total number of stack frames traversed, from the frame that threw the exception to the frame that handled the exception. | counter | `process`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,31 @@
# netframework_clrinterop collector
The netframework_clrinterop collector exposes metrics about interop between the dotnet framework and outside components.
|||
-|-
Metric name prefix | `netframework_clrinterop`
Classes | `Win32_PerfRawData_NETFramework_NETCLRInterop`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_...` | ... | counter/gauge/histogram/summary | ...
`wmi_netframework_clrinterop_com_callable_wrappers_total` | Displays the current number of COM callable wrappers (CCWs). A CCW is a proxy for a managed object being referenced from an unmanaged COM client. | counter | `process`
`wmi_netframework_clrinterop_interop_marshalling_total` | Displays the total number of times arguments and return values have been marshaled from managed to unmanaged code, and vice versa, since the application started. | counter | `process`
`wmi_netframework_clrinterop_interop_stubs_created_total` | Displays the current number of stubs created by the common language runtime. Stubs are responsible for marshaling arguments and return values from managed to unmanaged code, and vice versa, during a COM interop call or a platform invoke call. | counter | `process`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,32 @@
# netframework_clrjit collector
The netframework_clrjit collector exposes metrics about the dotnet Just-in-Time compiler.
|||
-|-
Metric name prefix | `netframework_clrjit`
Classes | `Win32_PerfRawData_NETFramework_NETCLRJit`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_...` | ... | counter/gauge/histogram/summary | ...
`wmi_netframework_clrjit_jit_methods_total` | Displays the total number of methods JIT-compiled since the application started. This counter does not include pre-JIT-compiled methods. | counter | `process`
`wmi_netframework_clrjit_jit_time_percent` | Displays the percentage of time spent in JIT compilation. This counter is updated at the end of every JIT compilation phase. A JIT compilation phase occurs when a method and its dependencies are compiled. | gauge | `process`
`wmi_netframework_clrjit_jit_standard_failures_total` | Displays the peak number of methods the JIT compiler has failed to compile since the application started. This failure can occur if the MSIL cannot be verified or if there is an internal error in the JIT compiler. | counter | `process`
`wmi_netframework_clrjit_jit_il_bytes_total` | Displays the total number of Microsoft intermediate language (MSIL) bytes compiled by the just-in-time (JIT) compiler since the application started | counter | `process`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,37 @@
# netframework_clrloading collector
The netframework_clrloading collector exposes metrics about the dotnet loader.
|||
-|-
Metric name prefix | `netframework_clrloading`
Classes | `Win32_PerfRawData_NETFramework_NETCLRLoading`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_...` | ... | counter/gauge/histogram/summary | ...
`wmi_netframework_clrloading_loader_heap_size_bytes` | Displays the current size, in bytes, of the memory committed by the class loader across all application domains. Committed memory is the physical space reserved in the disk paging file. | gauge | `process`
`wmi_netframework_clrloading_appdomains_loaded_current` | Displays the current number of application domains loaded in this application. | gauge | `process`
`wmi_netframework_clrloading_assemblies_loaded_current` | Displays the current number of assemblies loaded across all application domains in the currently running application. If the assembly is loaded as domain-neutral from multiple application domains, this counter is incremented only once. | gauge | `process`
`wmi_netframework_clrloading_classes_loaded_current` | Displays the current number of classes loaded in all assemblies. | gauge | `process`
`wmi_netframework_clrloading_appdomains_loaded_total` | Displays the peak number of application domains loaded since the application started. | counter | `process`
`wmi_netframework_clrloading_appdomains_unloaded_total` | Displays the total number of application domains unloaded since the application started. If an application domain is loaded and unloaded multiple times, this counter increments each time the application domain is unloaded. | counter | `process`
`wmi_netframework_clrloading_assemblies_loaded_total` | Displays the total number of assemblies loaded since the application started. If the assembly is loaded as domain-neutral from multiple application domains, this counter is incremented only once. | counter | `process`
`wmi_netframework_clrloading_classes_loaded_total` | Displays the cumulative number of classes loaded in all assemblies since the application started. | counter | `process`
`wmi_netframework_clrloading_class_load_failures_total` | Displays the peak number of classes that have failed to load since the application started. | counter | `process`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,35 @@
# netframework_clrlocksandthreads collector
The netframework_clrlocksandthreads collector exposes metrics about locks and threads in dotnet applications.
|||
-|-
Metric name prefix | `netframework_clrlocksandthreads`
Classes | `Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_...` | ... | counter/gauge/histogram/summary | ...
`wmi_netframework_clrlocksandthreads_current_queue_length` | Displays the total number of threads that are currently waiting to acquire a managed lock in the application. | gauge | `process`
`wmi_netframework_clrlocksandthreads_current_logical_threads` | Displays the number of current managed thread objects in the application. This counter maintains the count of both running and stopped threads. | gauge | `process`
`wmi_netframework_clrlocksandthreads_physical_threads_current` | Displays the number of native operating system threads created and owned by the common language runtime to act as underlying threads for managed thread objects. This counter's value does not include the threads used by the runtime in its internal operations; it is a subset of the threads in the operating system process. | gauge | `process`
`wmi_netframework_clrlocksandthreads_recognized_threads_current` | Displays the number of threads that are currently recognized by the runtime. These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once. | gauge | `process`
`wmi_netframework_clrlocksandthreads_recognized_threads_total` | Displays the total number of threads that have been recognized by the runtime since the application started. These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once. | counter | `process`
`wmi_netframework_clrlocksandthreads_queue_length_total` | Displays the total number of threads that waited to acquire a managed lock since the application started. | counter | `process`
`wmi_netframework_clrlocksandthreads_contentions_total` | Displays the total number of times that threads in the runtime have attempted to acquire a managed lock unsuccessfully. | counter | `process`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,40 @@
# netframework_clrmemory collector
The netframework_clrmemory collector exposes metrics about memory in dotnet applications.
|||
-|-
Metric name prefix | `netframework_clrmemory`
Classes | `Win32_PerfRawData_NETFramework_NETCLRMemory`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_...` | ... | counter/gauge/histogram/summary | ...
`wmi_netframework_clrmemory_allocated_bytes_total` | Displays the total number of bytes allocated on the garbage collection heap. | counter | `process`
`wmi_netframework_clrmemory_finalization_survivors` | Displays the number of garbage-collected objects that survive a collection because they are waiting to be finalized. | gauge | `process`
`wmi_netframework_clrmemory_heap_size_bytes` | Displays the maximum bytes that can be allocated; it does not indicate the current number of bytes allocated. | gauge | `process`
`wmi_netframework_clrmemory_promoted_bytes` | Displays the bytes that were promoted from the generation to the next one during the last GC. Memory is promoted when it survives a garbage collection. | gauge | `process`
`wmi_netframework_clrmemory_number_gc_handles` | Displays the current number of garbage collection handles in use. Garbage collection handles are handles to resources external to the common language runtime and the managed environment. | gauge | `process`
`wmi_netframework_clrmemory_collections_total` | Displays the number of times the generation objects are garbage collected since the application started. | counter | `process`
`wmi_netframework_clrmemory_induced_gc_total` | Displays the peak number of times garbage collection was performed because of an explicit call to GC.Collect. | counter | `process`
`wmi_netframework_clrmemory_number_pinned_objects` | Displays the number of pinned objects encountered in the last garbage collection. | gauge | `process`
`wmi_netframework_clrmemory_number_sink_blocksinuse` | Displays the current number of synchronization blocks in use. Synchronization blocks are per-object data structures allocated for storing synchronization information. They hold weak references to managed objects and must be scanned by the garbage collector. | gauge | `process`
`wmi_netframework_clrmemory_committed_bytes` | Displays the amount of virtual memory, in bytes, currently committed by the garbage collector. Committed memory is the physical memory for which space has been reserved in the disk paging file. | gauge | `process`
`wmi_netframework_clrmemory_reserved_bytes` | Displays the amount of virtual memory, in bytes, currently reserved by the garbage collector. Reserved memory is the virtual memory space reserved for the application when no disk or main memory pages have been used. | gauge | `process`
`wmi_netframework_clrmemory_gc_time_percent` | Displays the percentage of time that was spent performing a garbage collection in the last sample. | gauge | `process`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,33 @@
# netframework_clrremoting collector
The netframework_clrremoting collector exposes metrics about dotnet remoting.
|||
-|-
Metric name prefix | `netframework_clrremoting`
Classes | `Win32_PerfRawData_NETFramework_NETCLRRemoting`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_netframework_clrremoting_channels_total` | Displays the total number of remoting channels registered across all application domains since application started. | counter | `process`
`wmi_netframework_clrremoting_context_bound_classes_loaded` | Displays the current number of context-bound classes that are loaded. | gauge | `process`
`wmi_netframework_clrremoting_context_bound_objects_total` | Displays the total number of context-bound objects allocated. | counter | `process`
`wmi_netframework_clrremoting_context_proxies_total` | Displays the total number of remoting proxy objects in this process since it started. | counter | `process`
`wmi_netframework_clrremoting_contexts` | Displays the current number of remoting contexts in the application. | gauge | `process`
`wmi_netframework_clrremoting_remote_calls_total` | Displays the total number of remote procedure calls invoked since the application started. | counter | `process`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,31 @@
# netframework_clrsecurity collector
The netframework_clrsecurity collector exposes metrics about security checks in dotnet applications
|||
-|-
Metric name prefix | `netframework_clrsecurity`
Classes | `Win32_PerfRawData_NETFramework_NETCLRSecurity`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_netframework_clrsecurity_link_time_checks_total` | Displays the total number of link-time code access security checks since the application started. | counter | `process`
`wmi_netframework_clrsecurity_rt_checks_time_percent` | Displays the percentage of time spent performing runtime code access security checks in the last sample. | gauge | `process`
`wmi_netframework_clrsecurity_stack_walk_depth` | Displays the depth of the stack during that last runtime code access security check. | gauge | `process`
`wmi_netframework_clrsecurity_runtime_checks_total` | Displays the total number of runtime code access security checks performed since the application started. | counter | `process`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

39
docs/collector.os.md Normal file
View File

@@ -0,0 +1,39 @@
# os collector
The os collector exposes metrics about the operating system
|||
-|-
Metric name prefix | `os`
Classes | [`Win32_OperatingSystem`](https://msdn.microsoft.com/en-us/library/aa394239)
Enabled by default? | Yes
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_os_paging_limit_bytes` | _Not yet documented_ | gauge | None
`wmi_os_paging_free_bytes` | _Not yet documented_ | gauge | None
`wmi_os_physical_memory_free_bytes` | _Not yet documented_ | gauge | None
`wmi_os_time` | _Not yet documented_ | gauge | None
`wmi_os_timezone` | _Not yet documented_ | gauge | `timezone`
`wmi_os_processes` | _Not yet documented_ | gauge | None
`wmi_os_processes_limit` | _Not yet documented_ | gauge | None
`wmi_os_process_memory_limix_bytes` | _Not yet documented_ | gauge | None
`wmi_os_users` | _Not yet documented_ | gauge | None
`wmi_os_virtual_memory_bytes` | _Not yet documented_ | gauge | None
`wmi_os_visible_memory_bytes` | _Not yet documented_ | gauge | None
`wmi_os_virtual_memory_free_bytes` | _Not yet documented_ | gauge | None
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

46
docs/collector.process.md Normal file
View File

@@ -0,0 +1,46 @@
# process collector
The process collector exposes metrics about processes
|||
-|-
Metric name prefix | `process`
Classes | [`Win32_PerfRawData_PerfProc_Process`](https://msdn.microsoft.com/en-us/library/aa394323(v=vs.85).aspx)
Enabled by default? | No
## Flags
### `--collector.process.processes-where`
A WMI filter on which processes to include. Recommended to keep down number of returned metrics.
`%` is a wildcard, and can be used to match on substrings.
Example: `--collector.process.processes-where="Name LIKE 'firefox%'`
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_process_start_time` | _Not yet documented_ | gauge | `process`, `process_id`, `creating_process_id`
`wmi_process_cpu_time_total` | _Not yet documented_ | counter | `process`, `process_id`, `creating_process_id`
`wmi_process_handle_count` | _Not yet documented_ | gauge | `process`, `process_id`, `creating_process_id`
`wmi_process_io_bytes_total` | _Not yet documented_ | counter | `process`, `process_id`, `creating_process_id`
`wmi_process_io_operations_total` | _Not yet documented_ | counter | `process`, `process_id`, `creating_process_id`
`wmi_process_page_faults_total` | _Not yet documented_ | counter | `process`, `process_id`, `creating_process_id`
`wmi_process_page_file_bytes` | _Not yet documented_ | gauge | `process`, `process_id`, `creating_process_id`
`wmi_process_pool_bytes` | _Not yet documented_ | gauge | `process`, `process_id`, `creating_process_id`
`wmi_process_priority_base` | _Not yet documented_ | gauge | `process`, `process_id`, `creating_process_id`
`wmi_process_private_bytes` | _Not yet documented_ | gauge | `process`, `process_id`, `creating_process_id`
`wmi_process_thread_count` | _Not yet documented_ | gauge | `process`, `process_id`, `creating_process_id`
`wmi_process_virtual_bytes` | _Not yet documented_ | gauge | `process`, `process_id`, `creating_process_id`
`wmi_process_working_set` | _Not yet documented_ | gauge | `process`, `process_id`, `creating_process_id`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

75
docs/collector.service.md Normal file
View File

@@ -0,0 +1,75 @@
# service collector
The service collector exposes metrics about Windows Services
|||
-|-
Metric name prefix | `service`
Classes | [`Win32_Service`](https://msdn.microsoft.com/en-us/library/aa394418(v=vs.85).aspx)
Enabled by default? | Yes
## Flags
### `--collector.service.services-where`
A WMI filter on which services to include. Recommended to keep down number of returned metrics.
Example: `--collector.service.services-where="Name='wmi_exporter'"`
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_service_state` | The state of the service, 1 if the current state, 0 otherwise | gauge | name, state
`wmi_service_start_mode` | The start mode of the service, 1 if the current start mode, 0 otherwise | gauge | name, start_mode
`wmi_service_status` | The status of the service, 1 if the current status, 0 otherwise | gauge | name, status
For the values of the `state`, `start_mode` and `status` labels, see below.
### States
A service can be in the following states:
- `stopped`
- `start pending`
- `stop pending`
- `running`
- `continue pending`
- `pause pending`
- `paused`
- `unknown`
### Start modes
A service can have the following start modes:
- `boot`
- `system`
- `auto`
- `manual`
- `disabled`
### Status
A service can have any of the following statuses:
- `ok`
- `error`
- `degraded`
- `unknown`
- `pred fail`
- `starting`
- `stopping`
- `service`
- `stressed`
- `nonrecover`
- `no contact`
- `lost comm`
Note that there is some overlap with service state.
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

33
docs/collector.system.md Normal file
View File

@@ -0,0 +1,33 @@
# system collector
The system collector exposes metrics about ...
|||
-|-
Metric name prefix | `system`
Classes | [`Win32_PerfRawData_PerfOS_System`](https://web.archive.org/web/20050830140516/http://msdn.microsoft.com/library/en-us/wmisdk/wmi/win32_perfrawdata_perfos_system.asp)
Enabled by default? | Yes
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_system_context_switches_total` | _Not yet documented_ | counter | None
`wmi_system_exception_dispatches_total` | _Not yet documented_ | counter | None
`wmi_system_processor_queue_length` | _Not yet documented_ | gauge | None
`wmi_system_system_calls_total` | _Not yet documented_ | counter | None
`wmi_system_system_up_time` | _Not yet documented_ | gauge | None
`wmi_system_threads` | _Not yet documented_ | gauge | None
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

36
docs/collector.tcp.md Normal file
View File

@@ -0,0 +1,36 @@
# tcp collector
The tcp collector exposes metrics about the TCP/IPv4 network stack.
|||
-|-
Metric name prefix | `tcp`
Classes | [`Win32_PerfRawData_Tcpip_TCPv4`](https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx)
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_tcp_connection_failures` | _Not yet documented_ | counter | None
`wmi_tcp_connections_active` | _Not yet documented_ | counter | None
`wmi_tcp_connections_established` | _Not yet documented_ | counter | None
`wmi_tcp_connections_passive` | _Not yet documented_ | counter | None
`wmi_tcp_connections_reset` | _Not yet documented_ | counter | None
`wmi_tcp_segments_total` | _Not yet documented_ | counter | None
`wmi_tcp_segments_received_total` | _Not yet documented_ | counter | None
`wmi_tcp_segments_retransmitted_total` | _Not yet documented_ | counter | None
`wmi_tcp_segments_sent_total` | _Not yet documented_ | counter | None
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,56 @@
# textfile collector
The textfile collector exposes metrics from files written by other processes.
|||
-|-
Metric name prefix | `textfile`
Classes | None
Enabled by default? | Yes
## Flags
### `--collector.textfile.directory`
The directory containing the files to be ingested. Only files with the extension `.prom` are read.
Default value: `C:\Program Files\wmi_exporter\textfile_inputs`
Required: No
## Metrics
Metrics will primarily come from the files on disk. The below listed metrics
are collected to give information about the reading of the metrics themselves.
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_textfile_scrape_error` | 1 if there was an error opening or reading a file, 0 otherwise | gauge | None
`wmi_textfile_mtime_seconds` | Unix epoch-formatted mtime (modified time) of textfiles successfully read | gauge | file
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_
# Example use
This Powershell script, when run in the `collector.textfile.directory` (default `C:\Program Files\wmi_exporter\textfile_inputs`), generates a valid `.prom` file that should successfully ingested by wmi_exporter.
```Powershell
$alpha = 42
$beta = @{ left=3.1415; right=2.718281828; }
Set-Content -Path test1.prom -Encoding Ascii -NoNewline -Value ""
Add-Content -Path test1.prom -Encoding Ascii -NoNewline -Value "# HELP test_alpha_total Some random metric.`n"
Add-Content -Path test1.prom -Encoding Ascii -NoNewline -Value "# TYPE test_alpha_total counter`n"
Add-Content -Path test1.prom -Encoding Ascii -NoNewline -Value "test_alpha_total ${alpha}`n"
Add-Content -Path test1.prom -Encoding Ascii -NoNewline -Value "# HELP test_beta_bytes Some other metric.`n"
Add-Content -Path test1.prom -Encoding Ascii -NoNewline -Value "# TYPE test_beta_bytes gauge`n"
foreach ($k in $beta.Keys) {
Add-Content -Path test1.prom -Encoding Ascii -NoNewline -Value "test_beta_bytes{spin=""${k}""} $( $beta[$k] )`n"
}
```

46
docs/collector.vmware.md Normal file
View File

@@ -0,0 +1,46 @@
# vmware collector
The vmware collector exposes metrics about a VMware guest VM
|||
-|-
Metric name prefix | `vmware`
Classes | `Win32_PerfRawData_vmGuestLib_VMem`, `Win32_PerfRawData_vmGuestLib_VCPU`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`wmi_vmware_mem_active_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_ballooned_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_limit_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_mapped_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_overhead_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_reservation_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_shared_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_shared_saved_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_shares` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_swapped_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_target_size_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_mem_used_bytes` | _Not yet documented_ | gauge | None
`wmi_vmware_cpu_limit_mhz` | _Not yet documented_ | gauge | None
`wmi_vmware_cpu_reservation_mhz` | _Not yet documented_ | gauge | None
`wmi_vmware_cpu_shares` | _Not yet documented_ | gauge | None
`wmi_vmware_cpu_stolen_seconds_total` | _Not yet documented_ | counter | None
`wmi_vmware_cpu_time_seconds_total` | _Not yet documented_ | counter | None
`wmi_vmware_effective_vm_speed_mhz` | _Not yet documented_ | gauge | None
`wmi_vmware_host_processor_speed_mhz` | _Not yet documented_ | gauge | None
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -1,11 +1,10 @@
// +build windows
package main
import (
"flag"
"fmt"
"io"
"net/http"
"os"
"sort"
"strings"
"sync"
@@ -19,6 +18,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/log"
"github.com/prometheus/common/version"
"gopkg.in/alecthomas/kingpin.v2"
)
// WmiCollector implements the prometheus.Collector interface.
@@ -27,7 +27,7 @@ type WmiCollector struct {
}
const (
defaultCollectors = "cpu,cs,logical_disk,net,os,service,system"
defaultCollectors = "cpu,cs,logical_disk,net,os,service,system,textfile"
defaultCollectorsPlaceholder = "[defaults]"
serviceName = "wmi_exporter"
)
@@ -45,6 +45,16 @@ var (
[]string{"collector"},
nil,
)
// This can be removed when client_golang exposes this on Windows
// (See https://github.com/prometheus/client_golang/issues/376)
startTime = float64(time.Now().Unix())
startTimeDesc = prometheus.NewDesc(
"process_start_time_seconds",
"Start time of the process since unix epoch in seconds.",
nil,
nil,
)
)
// Describe sends all the descriptors of the collectors included to
@@ -66,6 +76,12 @@ func (coll WmiCollector) Collect(ch chan<- prometheus.Metric) {
wg.Done()
}(name, c)
}
ch <- prometheus.MustNewConstMetric(
startTimeDesc,
prometheus.CounterValue,
startTime,
)
wg.Wait()
}
@@ -87,10 +103,10 @@ func execute(name string, c collector.Collector, ch chan<- prometheus.Metric) {
var success float64
if err != nil {
log.Errorf("ERROR: %s collector failed after %fs: %s", name, duration.Seconds(), err)
log.Errorf("collector %s failed after %fs: %s", name, duration.Seconds(), err)
success = 0
} else {
log.Debugf("OK: %s collector succeeded after %fs.", name, duration.Seconds())
log.Debugf("collector %s succeeded after %fs.", name, duration.Seconds())
success = 1
}
ch <- prometheus.MustNewConstMetric(
@@ -117,7 +133,7 @@ func expandEnabledCollectors(enabled string) []string {
}
}
result := make([]string, 0, len(unique))
for s, _ := range unique {
for s := range unique {
result = append(result, s)
}
return result
@@ -154,23 +170,34 @@ func initWbem() {
if err != nil {
log.Fatal(err)
}
wmi.DefaultClient.AllowMissingFields = true
wmi.DefaultClient.SWbemServicesClient = s
}
func main() {
var (
showVersion = flag.Bool("version", false, "Print version information.")
listenAddress = flag.String("telemetry.addr", ":9182", "host:port for WMI exporter.")
metricsPath = flag.String("telemetry.path", "/metrics", "URL path for surfacing collected metrics.")
enabledCollectors = flag.String("collectors.enabled", filterAvailableCollectors(defaultCollectors), "Comma-separated list of collectors to use. Use '[default]' as a placeholder for all the collectors enabled by default")
printCollectors = flag.Bool("collectors.print", false, "If true, print available collectors and exit.")
listenAddress = kingpin.Flag(
"telemetry.addr",
"host:port for WMI exporter.",
).Default(":9182").String()
metricsPath = kingpin.Flag(
"telemetry.path",
"URL path for surfacing collected metrics.",
).Default("/metrics").String()
enabledCollectors = kingpin.Flag(
"collectors.enabled",
"Comma-separated list of collectors to use. Use '[defaults]' as a placeholder for all the collectors enabled by default.").
Default(filterAvailableCollectors(defaultCollectors)).String()
printCollectors = kingpin.Flag(
"collectors.print",
"If true, print available collectors and exit.",
).Bool()
)
flag.Parse()
if *showVersion {
fmt.Fprintln(os.Stdout, version.Print("wmi_exporter"))
os.Exit(0)
}
log.AddFlags(kingpin.CommandLine)
kingpin.Version(version.Print("wmi_exporter"))
kingpin.HelpFlag.Short('h')
kingpin.Parse()
if *printCollectors {
collectorNames := make(sort.StringSlice, 0, len(collector.Factories))
@@ -194,7 +221,12 @@ func main() {
stopCh := make(chan bool)
if !isInteractive {
go svc.Run(serviceName, &wmiExporterService{stopCh: stopCh})
go func() {
err = svc.Run(serviceName, &wmiExporterService{stopCh: stopCh})
if err != nil {
log.Errorf("Failed to start service: %v", err)
}
}()
}
collectors, err := loadCollectors(*enabledCollectors)
@@ -231,7 +263,10 @@ func main() {
func healthCheck(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
io.WriteString(w, `{"status":"ok"}`)
_, err := fmt.Fprintln(w, `{"status":"ok"}`)
if err != nil {
log.Debugf("Failed to write to stream: %v", err)
}
}
func keys(m map[string]collector.Collector) []string {

View File

@@ -1,3 +1,5 @@
// +build windows
package main
import (

28
gometalinter.config Normal file
View File

@@ -0,0 +1,28 @@
{
"Disable": [
"goconst",
"gocyclo",
"gosec",
"maligned",
"megacheck"
],
"Enable": [
"deadcode",
"errcheck",
"golint",
"gotype",
"gotypex",
"ineffassign",
"interfacer",
"structcheck",
"unconvert",
"varcheck",
"vet",
"vetshadow"
],
"Exclude": [
"don't use underscores in Go names",
"exported type .+ should have comment or be unexported",
"should be"
]
}

View File

@@ -1,12 +1,12 @@
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[String] $PathToExecutable,
[Parameter(Mandatory=$true)]
[String] $Version,
[Parameter(Mandatory=$false)]
[ValidateSet("amd64","386")]
[String] $Arch = "amd64"
[Parameter(Mandatory = $true)]
[String] $PathToExecutable,
[Parameter(Mandatory = $true)]
[String] $Version,
[Parameter(Mandatory = $false)]
[ValidateSet("amd64", "386")]
[String] $Arch = "amd64"
)
$ErrorActionPreference = "Stop"
@@ -15,34 +15,35 @@ $PathToExecutable = Resolve-Path $PathToExecutable
# Set working dir to this directory, reset previous on exit
Push-Location $PSScriptRoot
Trap {
# Reset working dir on error
Pop-Location
# Reset working dir on error
Pop-Location
}
if ($PSVersionTable.PSVersion.Major -lt 5) {
Write-Error "Powershell version 5 required"
exit 1
Write-Error "Powershell version 5 required"
exit 1
}
$wc = New-Object System.Net.WebClient
function Get-FileIfNotExists {
Param (
$Url,
$Destination
)
if(-not (Test-Path $Destination)) {
Write-Verbose "Downloading $Url"
$wc.DownloadFile($Url, $Destination)
}
else {
Write-Verbose "${Destination} already exists. Skipping."
}
Param (
$Url,
$Destination
)
if (-not (Test-Path $Destination)) {
Write-Verbose "Downloading $Url"
$wc.DownloadFile($Url, $Destination)
}
else {
Write-Verbose "${Destination} already exists. Skipping."
}
}
$sourceDir = mkdir -Force Source
mkdir -Force Work,Output | Out-Null
mkdir -Force Work, Output | Out-Null
Write-Verbose "Downloading WiX..."
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Get-FileIfNotExists "https://github.com/wixtoolset/wix3/releases/download/wix311rtm/wix311-binaries.zip" "$sourceDir\wix-binaries.zip"
mkdir -Force WiX | Out-Null
Expand-Archive -Path "${sourceDir}\wix-binaries.zip" -DestinationPath WiX -Force
@@ -50,8 +51,8 @@ Expand-Archive -Path "${sourceDir}\wix-binaries.zip" -DestinationPath WiX -Force
Copy-Item -Force $PathToExecutable Work/wmi_exporter.exe
Write-Verbose "Creating wmi_exporter-${Version}-${Arch}.msi"
$wixArch = @{"amd64"="x64"; "386"="x86"}[$Arch]
$wixOpts = "-ext WixFirewallExtension"
$wixArch = @{"amd64" = "x64"; "386" = "x86"}[$Arch]
$wixOpts = "-ext WixFirewallExtension -ext WixUtilExtension"
Invoke-Expression "WiX\candle.exe -nologo -arch $wixArch $wixOpts -out Work\wmi_exporter.wixobj -dVersion=`"$Version`" wmi_exporter.wxs"
Invoke-Expression "WiX\light.exe -nologo -spdb $wixOpts -out `"Output\wmi_exporter-${Version}-${Arch}.msi`" Work\wmi_exporter.wixobj"

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:fw="http://schemas.microsoft.com/wix/FirewallExtension">
xmlns:fw="http://schemas.microsoft.com/wix/FirewallExtension"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<?if $(sys.BUILDARCH)=x64 ?>
<?define PlatformProgramFiles = "ProgramFiles64Folder" ?>
<?else ?>
@@ -16,28 +17,42 @@
<MajorUpgrade Schedule="afterInstallExecute" DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." />
<Property Id="ENABLED_COLLECTORS" Secure="yes"/>
<SetProperty Id="CollectorsFlag" After="InstallFiles" Sequence="execute" Value="-collectors.enabled [ENABLED_COLLECTORS]">ENABLED_COLLECTORS</SetProperty>
<SetProperty Id="CollectorsFlag" After="InstallFiles" Sequence="execute" Value="--collectors.enabled [ENABLED_COLLECTORS]">ENABLED_COLLECTORS</SetProperty>
<Property Id="EXTRA_FLAGS" Secure="yes"/>
<SetProperty Id="ExtraFlags" After="InstallFiles" Sequence="execute" Value="[EXTRA_FLAGS]">EXTRA_FLAGS</SetProperty>
<Property Id="LISTEN_ADDR" Secure="yes" />
<Property Id="LISTEN_PORT" Secure="yes" Value="9182" />
<SetProperty Id="ListenFlag" After="InstallFiles" Sequence="execute" Value="-telemetry.addr [LISTEN_ADDR]:[LISTEN_PORT]">LISTEN_ADDR OR LISTEN_PORT</SetProperty>
<SetProperty Id="ListenFlag" After="InstallFiles" Sequence="execute" Value="--telemetry.addr [LISTEN_ADDR]:[LISTEN_PORT]">LISTEN_ADDR OR LISTEN_PORT</SetProperty>
<Property Id="METRICS_PATH" Secure="yes"/>
<SetProperty Id="MetricsPathFlag" After="InstallFiles" Sequence="execute" Value="-telemetry.path [METRICS_PATH]">METRICS_PATH</SetProperty>
<SetProperty Id="MetricsPathFlag" After="InstallFiles" Sequence="execute" Value="--telemetry.path [METRICS_PATH]">METRICS_PATH</SetProperty>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="$(var.PlatformProgramFiles)">
<Directory Id="APPLICATIONROOTDIRECTORY" Name="wmi_exporter" />
<Directory Id="APPLICATIONROOTDIRECTORY" Name="wmi_exporter">
<Directory Id="textfile_inputs" Name="textfile_inputs" />
</Directory>
</Directory>
</Directory>
<ComponentGroup Id="Files" Directory="APPLICATIONROOTDIRECTORY">
<Component>
<Property Id="TEXTFILE_DIR" Secure="yes"/>
<SetProperty Id="TextfileDirFlag" After="InstallFiles" Sequence="execute" Value="--collector.textfile.directory [TEXTFILE_DIR]">TEXTFILE_DIR</SetProperty>
<ComponentGroup Id="Files">
<Component Directory="APPLICATIONROOTDIRECTORY">
<File Id="wmi_exporter.exe" Name="wmi_exporter.exe" Source="Work\wmi_exporter.exe" KeyPath="yes">
<fw:FirewallException Id="MetricsEndpoint" Name="WMI Exporter (HTTP [LISTEN_PORT])" Description="WMI Exporter HTTP endpoint" Port="[LISTEN_PORT]" Protocol="tcp" Scope="any" IgnoreFailure="yes" />
</File>
<ServiceInstall Id="InstallExporterService" Name="wmi_exporter" ErrorControl="normal" Start="auto" Type="ownProcess" Arguments="-log.format logger:eventlog?name=wmi_exporter [CollectorsFlag] [ListenFlag] [MetricsPathFlag]" />
<ServiceInstall Id="InstallExporterService" Name="wmi_exporter" DisplayName="WMI exporter" Description="Exports Prometheus metrics from WMI queries" ErrorControl="normal" Start="auto" Type="ownProcess" Arguments="--log.format logger:eventlog?name=wmi_exporter [CollectorsFlag] [ListenFlag] [MetricsPathFlag] [TextfileDirFlag] [ExtraFlags]">
<util:ServiceConfig FirstFailureActionType="restart" SecondFailureActionType="restart" ThirdFailureActionType="restart" RestartServiceDelayInSeconds="5" />
</ServiceInstall>
<ServiceControl Id="ServiceStateControl" Name="wmi_exporter" Remove="uninstall" Start="install" Stop="both" />
<util:EventSource Log="Application" Name="wmi_exporter" EventMessageFile="%SystemRoot%\System32\EventCreate.exe" />
</Component>
<Component Id="CreateTextfileDirectory" Directory="textfile_inputs" Guid="d03ef58a-9cbf-4165-ad39-d143e9b27e14">
<CreateFolder />
</Component>
</ComponentGroup>
@@ -45,4 +60,4 @@
<ComponentGroupRef Id="Files" />
</Feature>
</Product>
</Wix>
</Wix>

View File

@@ -1,11 +1,8 @@
// returns data points from {{ .Class }}
// <add link to documentation here> - {{ .Class }} class
package collector
import (
"log"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
Factories["{{ .CollectorName | toLower }}"] = New{{ .CollectorName }}Collector
@@ -34,11 +31,13 @@ func New{{ .CollectorName }}Collector() (Collector, error) {
// to the provided prometheus Metric channel.
func (c *{{ .CollectorName }}Collector) Collect(ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Println("[ERROR] failed collecting {{ .CollectorName | toLower }} metrics:", desc, err)
log.Error("failed collecting {{ .CollectorName | toLower }} metrics:", desc, err)
return err
}
return nil
}
// {{ .Class }} docs:
// - <add link to documentation here>
type {{ .Class }} struct {
Name string
{{ range $m := .Members }}
@@ -47,7 +46,7 @@ type {{ .Class }} struct {
}
func (c *{{ .CollectorName }}Collector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []{{ .Class }}
q := wmi.CreateQuery(&dst, "")
q := queryAll(&dst)
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}

View File

@@ -25,7 +25,7 @@ func main() {
panic(err)
}
var data TemplateData
if err := json.Unmarshal(bytes, &data); err != nil {
if err = json.Unmarshal(bytes, &data); err != nil {
panic(err)
}
@@ -33,11 +33,11 @@ func main() {
"toLower": strings.ToLower,
"toSnakeCase": toSnakeCase,
}
template, err := template.New("template").Funcs(funcs).ParseFiles("collector.template")
tmpl, err := template.New("template").Funcs(funcs).ParseFiles("collector.template")
if err != nil {
panic(err)
}
err = template.ExecuteTemplate(os.Stdout, "collector.template", data)
err = tmpl.ExecuteTemplate(os.Stdout, "collector.template", data)
if err != nil {
panic(err)
}

View File

@@ -1,66 +0,0 @@
# 0.10.0
* feature: Add a test hook (#180)
* feature: `ParseLevel` is now case-insensitive (#326)
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
* performance: avoid re-allocations on `WithFields` (#335)
# 0.9.0
* logrus/text_formatter: don't emit empty msg
* logrus/hooks/airbrake: move out of main repository
* logrus/hooks/sentry: move out of main repository
* logrus/hooks/papertrail: move out of main repository
* logrus/hooks/bugsnag: move out of main repository
* logrus/core: run tests with `-race`
* logrus/core: detect TTY based on `stderr`
* logrus/core: support `WithError` on logger
* logrus/core: Solaris support
# 0.8.7
* logrus/core: fix possible race (#216)
* logrus/doc: small typo fixes and doc improvements
# 0.8.6
* hooks/raven: allow passing an initialized client
# 0.8.5
* logrus/core: revert #208
# 0.8.4
* formatter/text: fix data race (#218)
# 0.8.3
* logrus/core: fix entry log level (#208)
* logrus/core: improve performance of text formatter by 40%
* logrus/core: expose `LevelHooks` type
* logrus/core: add support for DragonflyBSD and NetBSD
* formatter/text: print structs more verbosely
# 0.8.2
* logrus: fix more Fatal family functions
# 0.8.1
* logrus: fix not exiting on `Fatalf` and `Fatalln`
# 0.8.0
* logrus: defaults to stderr instead of stdout
* hooks/sentry: add special field for `*http.Request`
* formatter/text: ignore Windows for colors
# 0.7.3
* formatter/\*: allow configuration of timestamp layout
# 0.7.2
* formatter/text: Add configuration option for time format (#158)

View File

@@ -1,421 +0,0 @@
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>&nbsp;[![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus)&nbsp;[![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus)
Logrus is a structured logger for Go (golang), completely API compatible with
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
yet stable (pre 1.0). Logrus itself is completely stable and has been used in
many large deployments. The core API is unlikely to change much but please
version control your Logrus to make sure you aren't fetching latest `master` on
every build.**
Nicely color-coded in development (when a TTY is attached, otherwise just
plain text):
![Colored](http://i.imgur.com/PY7qMwd.png)
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
or Splunk:
```json
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
{"level":"warning","msg":"The group's number increased tremendously!",
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
```
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
attached, the output is compatible with the
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
```text
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
exit status 1
```
#### Example
The simplest way to use Logrus is simply the package-level exported logger:
```go
package main
import (
log "github.com/Sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
```
Note that it's completely api-compatible with the stdlib logger, so you can
replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
and you'll now have the flexibility of Logrus. You can customize it all you
want:
```go
package main
import (
"os"
log "github.com/Sirupsen/logrus"
)
func init() {
// Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{})
// Output to stderr instead of stdout, could also be a file.
log.SetOutput(os.Stderr)
// Only log the warning severity or above.
log.SetLevel(log.WarnLevel)
}
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(log.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(log.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
// A common pattern is to re-use fields between logging statements by re-using
// the logrus.Entry returned from WithFields()
contextLogger := log.WithFields(log.Fields{
"common": "this is a common field",
"other": "I also should be logged always",
})
contextLogger.Info("I'll be logged with common and other field")
contextLogger.Info("Me too")
}
```
For more advanced usage such as logging to multiple locations from the same
application, you can also create an instance of the `logrus` Logger:
```go
package main
import (
"github.com/Sirupsen/logrus"
)
// Create a new instance of the logger. You can have any number of instances.
var log = logrus.New()
func main() {
// The API for setting attributes is a little different than the package level
// exported logger. See Godoc.
log.Out = os.Stderr
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
}
```
#### Fields
Logrus encourages careful, structured logging though logging fields instead of
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
to send event %s to topic %s with key %d")`, you should log the much more
discoverable:
```go
log.WithFields(log.Fields{
"event": event,
"topic": topic,
"key": key,
}).Fatal("Failed to send event")
```
We've found this API forces you to think about logging in a way that produces
much more useful logging messages. We've been in countless situations where just
a single added field to a log statement that was already there would've saved us
hours. The `WithFields` call is optional.
In general, with Logrus using any of the `printf`-family functions should be
seen as a hint you should add a field, however, you can still use the
`printf`-family functions with Logrus.
#### Hooks
You can add hooks for logging levels. For example to send errors to an exception
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
multiple places simultaneously, e.g. syslog.
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
`init`:
```go
import (
log "github.com/Sirupsen/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
"log/syslog"
)
func init() {
// Use the Airbrake hook to report errors that have Error severity or above to
// an exception tracker. You can create custom hooks, see the Hooks section.
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
log.Error("Unable to connect to local syslog daemon")
} else {
log.AddHook(hook)
}
}
```
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
| Hook | Description |
| ----- | ----------- |
| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) |
| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)|
| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) |
| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) |
#### Level logging
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
```go
log.Debug("Useful debugging information.")
log.Info("Something noteworthy happened!")
log.Warn("You should probably take a look at this.")
log.Error("Something failed but I'm not quitting.")
// Calls os.Exit(1) after logging
log.Fatal("Bye.")
// Calls panic() after logging
log.Panic("I'm bailing.")
```
You can set the logging level on a `Logger`, then it will only log entries with
that severity or anything above it:
```go
// Will log anything that is info or above (warn, error, fatal, panic). Default.
log.SetLevel(log.InfoLevel)
```
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
environment if your application has that.
#### Entries
Besides the fields added with `WithField` or `WithFields` some fields are
automatically added to all logging events:
1. `time`. The timestamp when the entry was created.
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
the `AddFields` call. E.g. `Failed to send event.`
3. `level`. The logging level. E.g. `info`.
#### Environments
Logrus has no notion of environment.
If you wish for hooks and formatters to only be used in specific environments,
you should handle that yourself. For example, if your application has a global
variable `Environment`, which is a string representation of the environment you
could do:
```go
import (
log "github.com/Sirupsen/logrus"
)
init() {
// do something here to set environment depending on an environment variable
// or command-line flag
if Environment == "production" {
log.SetFormatter(&log.JSONFormatter{})
} else {
// The TextFormatter is default, you don't actually have to do this.
log.SetFormatter(&log.TextFormatter{})
}
}
```
This configuration is how `logrus` was intended to be used, but JSON in
production is mostly only useful if you do log aggregation with tools like
Splunk or Logstash.
#### Formatters
The built-in logging formatters are:
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
without colors.
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
field to `true`. To force no colored output even if there is a TTY set the
`DisableColors` field to `true`
* `logrus.JSONFormatter`. Logs fields as JSON.
Third party logging formatters:
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
You can define your formatter by implementing the `Formatter` interface,
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
`Fields` type (`map[string]interface{}`) with all your fields as well as the
default ones (see Entries section above):
```go
type MyJSONFormatter struct {
}
log.SetFormatter(new(MyJSONFormatter))
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
// Note this doesn't include Time, Level and Message which are available on
// the Entry. Consult `godoc` on information about those fields or read the
// source of the official loggers.
serialized, err := json.Marshal(entry.Data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}
```
#### Logger as an `io.Writer`
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
```go
w := logger.Writer()
defer w.Close()
srv := http.Server{
// create a stdlib log.Logger that writes to
// logrus.Logger.
ErrorLog: log.New(w, "", 0),
}
```
Each line written to that writer will be printed the usual way, using formatters
and hooks. The level for those entries is `info`.
#### Rotation
Log rotation is not provided with Logrus. Log rotation should be done by an
external program (like `logrotate(8)`) that can compress and delete old log
entries. It should not be a feature of the application-level logger.
#### Tools
| Tool | Description |
| ---- | ----------- |
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
#### Testing
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
```go
logger, hook := NewNullLogger()
logger.Error("Hello error")
assert.Equal(1, len(hook.Entries))
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
assert.Equal("Hello error", hook.LastEntry().Message)
hook.Reset()
assert.Nil(hook.LastEntry())
```
#### Fatal handlers
Logrus can register one or more functions that will be called when any `fatal`
level message is logged. The registered handlers will be executed before
logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
```
...
handler := func() {
// gracefully shutdown something...
}
logrus.RegisterExitHandler(handler)
...
```
#### Thread safty
By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs.
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
Situation when locking is not needed includes:
* You have no hooks registered, or hooks calling is already thread-safe.
* Writing to logger.Out is already thread-safe, for example:
1) logger.Out is protected by locks.
2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)

View File

@@ -1,41 +0,0 @@
package logrus
import (
"encoding/json"
"fmt"
)
type JSONFormatter struct {
// TimestampFormat sets the format used for marshaling timestamps.
TimestampFormat string
}
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
data := make(Fields, len(entry.Data)+3)
for k, v := range entry.Data {
switch v := v.(type) {
case error:
// Otherwise errors are ignored by `encoding/json`
// https://github.com/Sirupsen/logrus/issues/137
data[k] = v.Error()
default:
data[k] = v
}
}
prefixFieldClashes(data)
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
timestampFormat = DefaultTimestampFormat
}
data["time"] = entry.Time.Format(timestampFormat)
data["msg"] = entry.Message
data["level"] = entry.Level.String()
serialized, err := json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}

View File

@@ -1,8 +0,0 @@
// +build appengine
package logrus
// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool {
return true
}

View File

@@ -1,10 +0,0 @@
// +build darwin freebsd openbsd netbsd dragonfly
// +build !appengine
package logrus
import "syscall"
const ioctlReadTermios = syscall.TIOCGETA
type Termios syscall.Termios

View File

@@ -1,22 +0,0 @@
// Based on ssh/terminal:
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux darwin freebsd openbsd netbsd dragonfly
// +build !appengine
package logrus
import (
"syscall"
"unsafe"
)
// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool {
fd := syscall.Stderr
var termios Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}

View File

@@ -1,15 +0,0 @@
// +build solaris,!appengine
package logrus
import (
"os"
"golang.org/x/sys/unix"
)
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal() bool {
_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
return err == nil
}

View File

@@ -1,27 +0,0 @@
// Based on ssh/terminal:
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows,!appengine
package logrus
import (
"syscall"
"unsafe"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var (
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
)
// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool {
fd := syscall.Stderr
var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0
}

View File

@@ -1,6 +0,0 @@
wmi
===
Package wmi provides a WQL interface to Windows WMI.
Note: It interfaces with WMI on the local machine, therefore it only runs on Windows.

View File

@@ -370,32 +370,50 @@ func (c *Client) loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismat
}
}
default:
// Only support []string slices for now
if f.Kind() == reflect.Slice && f.Type().Elem().Kind() == reflect.String {
safeArray := prop.ToArray()
if safeArray != nil {
arr := safeArray.ToValueArray()
fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr))
for i, v := range arr {
s := fArr.Index(i)
s.SetString(v.(string))
if f.Kind() == reflect.Slice {
switch f.Type().Elem().Kind() {
case reflect.String:
safeArray := prop.ToArray()
if safeArray != nil {
arr := safeArray.ToValueArray()
fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr))
for i, v := range arr {
s := fArr.Index(i)
s.SetString(v.(string))
}
f.Set(fArr)
}
case reflect.Uint8:
safeArray := prop.ToArray()
if safeArray != nil {
arr := safeArray.ToValueArray()
fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr))
for i, v := range arr {
s := fArr.Index(i)
s.SetUint(reflect.ValueOf(v).Uint())
}
f.Set(fArr)
}
default:
return &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: fmt.Sprintf("unsupported slice type (%T)", val),
}
}
} else {
typeof := reflect.TypeOf(val)
if typeof == nil && (isPtr || c.NonePtrZero) {
if (isPtr && c.PtrNil) || (!isPtr && c.NonePtrZero) {
of.Set(reflect.Zero(of.Type()))
}
f.Set(fArr)
break
}
}
typeof := reflect.TypeOf(val)
if typeof == nil && (isPtr || c.NonePtrZero) {
if (isPtr && c.PtrNil) || (!isPtr && c.NonePtrZero) {
of.Set(reflect.Zero(of.Type()))
return &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: fmt.Sprintf("unsupported type (%T)", val),
}
break
}
return &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: fmt.Sprintf("unsupported type (%T)", val),
}
}
}

27
vendor/github.com/alecthomas/template/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

406
vendor/github.com/alecthomas/template/doc.go generated vendored Normal file
View File

@@ -0,0 +1,406 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package template implements data-driven templates for generating textual output.
To generate HTML output, see package html/template, which has the same interface
as this package but automatically secures HTML output against certain attacks.
Templates are executed by applying them to a data structure. Annotations in the
template refer to elements of the data structure (typically a field of a struct
or a key in a map) to control execution and derive values to be displayed.
Execution of the template walks the structure and sets the cursor, represented
by a period '.' and called "dot", to the value at the current location in the
structure as execution proceeds.
The input text for a template is UTF-8-encoded text in any format.
"Actions"--data evaluations or control structures--are delimited by
"{{" and "}}"; all text outside actions is copied to the output unchanged.
Actions may not span newlines, although comments can.
Once parsed, a template may be executed safely in parallel.
Here is a trivial example that prints "17 items are made of wool".
type Inventory struct {
Material string
Count uint
}
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }
More intricate examples appear below.
Actions
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
data, defined in detail below.
*/
// {{/* a comment */}}
// A comment; discarded. May contain newlines.
// Comments do not nest and must start and end at the
// delimiters, as shown here.
/*
{{pipeline}}
The default textual representation of the value of the pipeline
is copied to the output.
{{if pipeline}} T1 {{end}}
If the value of the pipeline is empty, no output is generated;
otherwise, T1 is executed. The empty values are false, 0, any
nil pointer or interface value, and any array, slice, map, or
string of length zero.
Dot is unaffected.
{{if pipeline}} T1 {{else}} T0 {{end}}
If the value of the pipeline is empty, T0 is executed;
otherwise, T1 is executed. Dot is unaffected.
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
To simplify the appearance of if-else chains, the else action
of an if may include another if directly; the effect is exactly
the same as writing
{{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
{{range pipeline}} T1 {{end}}
The value of the pipeline must be an array, slice, map, or channel.
If the value of the pipeline has length zero, nothing is output;
otherwise, dot is set to the successive elements of the array,
slice, or map and T1 is executed. If the value is a map and the
keys are of basic type with a defined order ("comparable"), the
elements will be visited in sorted key order.
{{range pipeline}} T1 {{else}} T0 {{end}}
The value of the pipeline must be an array, slice, map, or channel.
If the value of the pipeline has length zero, dot is unaffected and
T0 is executed; otherwise, dot is set to the successive elements
of the array, slice, or map and T1 is executed.
{{template "name"}}
The template with the specified name is executed with nil data.
{{template "name" pipeline}}
The template with the specified name is executed with dot set
to the value of the pipeline.
{{with pipeline}} T1 {{end}}
If the value of the pipeline is empty, no output is generated;
otherwise, dot is set to the value of the pipeline and T1 is
executed.
{{with pipeline}} T1 {{else}} T0 {{end}}
If the value of the pipeline is empty, dot is unaffected and T0
is executed; otherwise, dot is set to the value of the pipeline
and T1 is executed.
Arguments
An argument is a simple value, denoted by one of the following.
- A boolean, string, character, integer, floating-point, imaginary
or complex constant in Go syntax. These behave like Go's untyped
constants, although raw strings may not span newlines.
- The keyword nil, representing an untyped Go nil.
- The character '.' (period):
.
The result is the value of dot.
- A variable name, which is a (possibly empty) alphanumeric string
preceded by a dollar sign, such as
$piOver2
or
$
The result is the value of the variable.
Variables are described below.
- The name of a field of the data, which must be a struct, preceded
by a period, such as
.Field
The result is the value of the field. Field invocations may be
chained:
.Field1.Field2
Fields can also be evaluated on variables, including chaining:
$x.Field1.Field2
- The name of a key of the data, which must be a map, preceded
by a period, such as
.Key
The result is the map element value indexed by the key.
Key invocations may be chained and combined with fields to any
depth:
.Field1.Key1.Field2.Key2
Although the key must be an alphanumeric identifier, unlike with
field names they do not need to start with an upper case letter.
Keys can also be evaluated on variables, including chaining:
$x.key1.key2
- The name of a niladic method of the data, preceded by a period,
such as
.Method
The result is the value of invoking the method with dot as the
receiver, dot.Method(). Such a method must have one return value (of
any type) or two return values, the second of which is an error.
If it has two and the returned error is non-nil, execution terminates
and an error is returned to the caller as the value of Execute.
Method invocations may be chained and combined with fields and keys
to any depth:
.Field1.Key1.Method1.Field2.Key2.Method2
Methods can also be evaluated on variables, including chaining:
$x.Method1.Field
- The name of a niladic function, such as
fun
The result is the value of invoking the function, fun(). The return
types and values behave as in methods. Functions and function
names are described below.
- A parenthesized instance of one the above, for grouping. The result
may be accessed by a field or map key invocation.
print (.F1 arg1) (.F2 arg2)
(.StructValuedMethod "arg").Field
Arguments may evaluate to any type; if they are pointers the implementation
automatically indirects to the base type when required.
If an evaluation yields a function value, such as a function-valued
field of a struct, the function is not invoked automatically, but it
can be used as a truth value for an if action and the like. To invoke
it, use the call function, defined below.
A pipeline is a possibly chained sequence of "commands". A command is a simple
value (argument) or a function or method call, possibly with multiple arguments:
Argument
The result is the value of evaluating the argument.
.Method [Argument...]
The method can be alone or the last element of a chain but,
unlike methods in the middle of a chain, it can take arguments.
The result is the value of calling the method with the
arguments:
dot.Method(Argument1, etc.)
functionName [Argument...]
The result is the value of calling the function associated
with the name:
function(Argument1, etc.)
Functions and function names are described below.
Pipelines
A pipeline may be "chained" by separating a sequence of commands with pipeline
characters '|'. In a chained pipeline, the result of the each command is
passed as the last argument of the following command. The output of the final
command in the pipeline is the value of the pipeline.
The output of a command will be either one value or two values, the second of
which has type error. If that second value is present and evaluates to
non-nil, execution terminates and the error is returned to the caller of
Execute.
Variables
A pipeline inside an action may initialize a variable to capture the result.
The initialization has syntax
$variable := pipeline
where $variable is the name of the variable. An action that declares a
variable produces no output.
If a "range" action initializes a variable, the variable is set to the
successive elements of the iteration. Also, a "range" may declare two
variables, separated by a comma:
range $index, $element := pipeline
in which case $index and $element are set to the successive values of the
array/slice index or map key and element, respectively. Note that if there is
only one variable, it is assigned the element; this is opposite to the
convention in Go range clauses.
A variable's scope extends to the "end" action of the control structure ("if",
"with", or "range") in which it is declared, or to the end of the template if
there is no such control structure. A template invocation does not inherit
variables from the point of its invocation.
When execution begins, $ is set to the data argument passed to Execute, that is,
to the starting value of dot.
Examples
Here are some example one-line templates demonstrating pipelines and variables.
All produce the quoted word "output":
{{"\"output\""}}
A string constant.
{{`"output"`}}
A raw string constant.
{{printf "%q" "output"}}
A function call.
{{"output" | printf "%q"}}
A function call whose final argument comes from the previous
command.
{{printf "%q" (print "out" "put")}}
A parenthesized argument.
{{"put" | printf "%s%s" "out" | printf "%q"}}
A more elaborate call.
{{"output" | printf "%s" | printf "%q"}}
A longer chain.
{{with "output"}}{{printf "%q" .}}{{end}}
A with action using dot.
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
A with action that creates and uses a variable.
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
A with action that uses the variable in another action.
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
The same, but pipelined.
Functions
During execution functions are found in two function maps: first in the
template, then in the global function map. By default, no functions are defined
in the template but the Funcs method can be used to add them.
Predefined global functions are named as follows.
and
Returns the boolean AND of its arguments by returning the
first empty argument or the last argument, that is,
"and x y" behaves as "if x then y else x". All the
arguments are evaluated.
call
Returns the result of calling the first argument, which
must be a function, with the remaining arguments as parameters.
Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where
Y is a func-valued field, map entry, or the like.
The first argument must be the result of an evaluation
that yields a value of function type (as distinct from
a predefined function such as print). The function must
return either one or two result values, the second of which
is of type error. If the arguments don't match the function
or the returned error value is non-nil, execution stops.
html
Returns the escaped HTML equivalent of the textual
representation of its arguments.
index
Returns the result of indexing its first argument by the
following arguments. Thus "index x 1 2 3" is, in Go syntax,
x[1][2][3]. Each indexed item must be a map, slice, or array.
js
Returns the escaped JavaScript equivalent of the textual
representation of its arguments.
len
Returns the integer length of its argument.
not
Returns the boolean negation of its single argument.
or
Returns the boolean OR of its arguments by returning the
first non-empty argument or the last argument, that is,
"or x y" behaves as "if x then x else y". All the
arguments are evaluated.
print
An alias for fmt.Sprint
printf
An alias for fmt.Sprintf
println
An alias for fmt.Sprintln
urlquery
Returns the escaped value of the textual representation of
its arguments in a form suitable for embedding in a URL query.
The boolean functions take any zero value to be false and a non-zero
value to be true.
There is also a set of binary comparison operators defined as
functions:
eq
Returns the boolean truth of arg1 == arg2
ne
Returns the boolean truth of arg1 != arg2
lt
Returns the boolean truth of arg1 < arg2
le
Returns the boolean truth of arg1 <= arg2
gt
Returns the boolean truth of arg1 > arg2
ge
Returns the boolean truth of arg1 >= arg2
For simpler multi-way equality tests, eq (only) accepts two or more
arguments and compares the second and subsequent to the first,
returning in effect
arg1==arg2 || arg1==arg3 || arg1==arg4 ...
(Unlike with || in Go, however, eq is a function call and all the
arguments will be evaluated.)
The comparison functions work on basic types only (or named basic
types, such as "type Celsius float32"). They implement the Go rules
for comparison of values, except that size and exact type are
ignored, so any integer value, signed or unsigned, may be compared
with any other integer value. (The arithmetic value is compared,
not the bit pattern, so all negative integers are less than all
unsigned integers.) However, as usual, one may not compare an int
with a float32 and so on.
Associated templates
Each template is named by a string specified when it is created. Also, each
template is associated with zero or more other templates that it may invoke by
name; such associations are transitive and form a name space of templates.
A template may use a template invocation to instantiate another associated
template; see the explanation of the "template" action above. The name must be
that of a template associated with the template that contains the invocation.
Nested template definitions
When parsing a template, another template may be defined and associated with the
template being parsed. Template definitions must appear at the top level of the
template, much like global variables in a Go program.
The syntax of such definitions is to surround each template declaration with a
"define" and "end" action.
The define action names the template being created by providing a string
constant. Here is a simple example:
`{{define "T1"}}ONE{{end}}
{{define "T2"}}TWO{{end}}
{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
{{template "T3"}}`
This defines two templates, T1 and T2, and a third T3 that invokes the other two
when it is executed. Finally it invokes T3. If executed this template will
produce the text
ONE TWO
By construction, a template may reside in only one association. If it's
necessary to have a template addressable from multiple associations, the
template definition must be parsed multiple times to create distinct *Template
values, or must be copied with the Clone or AddParseTree method.
Parse may be called multiple times to assemble the various associated templates;
see the ParseFiles and ParseGlob functions and methods for simple ways to parse
related templates stored in files.
A template may be executed directly or through ExecuteTemplate, which executes
an associated template identified by name. To invoke our example above, we
might write,
err := tmpl.Execute(os.Stdout, "no data needed")
if err != nil {
log.Fatalf("execution failed: %s", err)
}
or to invoke a particular template explicitly by name,
err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed")
if err != nil {
log.Fatalf("execution failed: %s", err)
}
*/
package template

845
vendor/github.com/alecthomas/template/exec.go generated vendored Normal file
View File

@@ -0,0 +1,845 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package template
import (
"bytes"
"fmt"
"io"
"reflect"
"runtime"
"sort"
"strings"
"github.com/alecthomas/template/parse"
)
// state represents the state of an execution. It's not part of the
// template so that multiple executions of the same template
// can execute in parallel.
type state struct {
tmpl *Template
wr io.Writer
node parse.Node // current node, for errors
vars []variable // push-down stack of variable values.
}
// variable holds the dynamic value of a variable such as $, $x etc.
type variable struct {
name string
value reflect.Value
}
// push pushes a new variable on the stack.
func (s *state) push(name string, value reflect.Value) {
s.vars = append(s.vars, variable{name, value})
}
// mark returns the length of the variable stack.
func (s *state) mark() int {
return len(s.vars)
}
// pop pops the variable stack up to the mark.
func (s *state) pop(mark int) {
s.vars = s.vars[0:mark]
}
// setVar overwrites the top-nth variable on the stack. Used by range iterations.
func (s *state) setVar(n int, value reflect.Value) {
s.vars[len(s.vars)-n].value = value
}
// varValue returns the value of the named variable.
func (s *state) varValue(name string) reflect.Value {
for i := s.mark() - 1; i >= 0; i-- {
if s.vars[i].name == name {
return s.vars[i].value
}
}
s.errorf("undefined variable: %s", name)
return zero
}
var zero reflect.Value
// at marks the state to be on node n, for error reporting.
func (s *state) at(node parse.Node) {
s.node = node
}
// doublePercent returns the string with %'s replaced by %%, if necessary,
// so it can be used safely inside a Printf format string.
func doublePercent(str string) string {
if strings.Contains(str, "%") {
str = strings.Replace(str, "%", "%%", -1)
}
return str
}
// errorf formats the error and terminates processing.
func (s *state) errorf(format string, args ...interface{}) {
name := doublePercent(s.tmpl.Name())
if s.node == nil {
format = fmt.Sprintf("template: %s: %s", name, format)
} else {
location, context := s.tmpl.ErrorContext(s.node)
format = fmt.Sprintf("template: %s: executing %q at <%s>: %s", location, name, doublePercent(context), format)
}
panic(fmt.Errorf(format, args...))
}
// errRecover is the handler that turns panics into returns from the top
// level of Parse.
func errRecover(errp *error) {
e := recover()
if e != nil {
switch err := e.(type) {
case runtime.Error:
panic(e)
case error:
*errp = err
default:
panic(e)
}
}
}
// ExecuteTemplate applies the template associated with t that has the given name
// to the specified data object and writes the output to wr.
// If an error occurs executing the template or writing its output,
// execution stops, but partial results may already have been written to
// the output writer.
// A template may be executed safely in parallel.
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
tmpl := t.tmpl[name]
if tmpl == nil {
return fmt.Errorf("template: no template %q associated with template %q", name, t.name)
}
return tmpl.Execute(wr, data)
}
// Execute applies a parsed template to the specified data object,
// and writes the output to wr.
// If an error occurs executing the template or writing its output,
// execution stops, but partial results may already have been written to
// the output writer.
// A template may be executed safely in parallel.
func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
defer errRecover(&err)
value := reflect.ValueOf(data)
state := &state{
tmpl: t,
wr: wr,
vars: []variable{{"$", value}},
}
t.init()
if t.Tree == nil || t.Root == nil {
var b bytes.Buffer
for name, tmpl := range t.tmpl {
if tmpl.Tree == nil || tmpl.Root == nil {
continue
}
if b.Len() > 0 {
b.WriteString(", ")
}
fmt.Fprintf(&b, "%q", name)
}
var s string
if b.Len() > 0 {
s = "; defined templates are: " + b.String()
}
state.errorf("%q is an incomplete or empty template%s", t.Name(), s)
}
state.walk(value, t.Root)
return
}
// Walk functions step through the major pieces of the template structure,
// generating output as they go.
func (s *state) walk(dot reflect.Value, node parse.Node) {
s.at(node)
switch node := node.(type) {
case *parse.ActionNode:
// Do not pop variables so they persist until next end.
// Also, if the action declares variables, don't print the result.
val := s.evalPipeline(dot, node.Pipe)
if len(node.Pipe.Decl) == 0 {
s.printValue(node, val)
}
case *parse.IfNode:
s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
case *parse.ListNode:
for _, node := range node.Nodes {
s.walk(dot, node)
}
case *parse.RangeNode:
s.walkRange(dot, node)
case *parse.TemplateNode:
s.walkTemplate(dot, node)
case *parse.TextNode:
if _, err := s.wr.Write(node.Text); err != nil {
s.errorf("%s", err)
}
case *parse.WithNode:
s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)
default:
s.errorf("unknown node: %s", node)
}
}
// walkIfOrWith walks an 'if' or 'with' node. The two control structures
// are identical in behavior except that 'with' sets dot.
func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) {
defer s.pop(s.mark())
val := s.evalPipeline(dot, pipe)
truth, ok := isTrue(val)
if !ok {
s.errorf("if/with can't use %v", val)
}
if truth {
if typ == parse.NodeWith {
s.walk(val, list)
} else {
s.walk(dot, list)
}
} else if elseList != nil {
s.walk(dot, elseList)
}
}
// isTrue reports whether the value is 'true', in the sense of not the zero of its type,
// and whether the value has a meaningful truth value.
func isTrue(val reflect.Value) (truth, ok bool) {
if !val.IsValid() {
// Something like var x interface{}, never set. It's a form of nil.
return false, true
}
switch val.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
truth = val.Len() > 0
case reflect.Bool:
truth = val.Bool()
case reflect.Complex64, reflect.Complex128:
truth = val.Complex() != 0
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
truth = !val.IsNil()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
truth = val.Int() != 0
case reflect.Float32, reflect.Float64:
truth = val.Float() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
truth = val.Uint() != 0
case reflect.Struct:
truth = true // Struct values are always true.
default:
return
}
return truth, true
}
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
s.at(r)
defer s.pop(s.mark())
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
// mark top of stack before any variables in the body are pushed.
mark := s.mark()
oneIteration := func(index, elem reflect.Value) {
// Set top var (lexically the second if there are two) to the element.
if len(r.Pipe.Decl) > 0 {
s.setVar(1, elem)
}
// Set next var (lexically the first if there are two) to the index.
if len(r.Pipe.Decl) > 1 {
s.setVar(2, index)
}
s.walk(elem, r.List)
s.pop(mark)
}
switch val.Kind() {
case reflect.Array, reflect.Slice:
if val.Len() == 0 {
break
}
for i := 0; i < val.Len(); i++ {
oneIteration(reflect.ValueOf(i), val.Index(i))
}
return
case reflect.Map:
if val.Len() == 0 {
break
}
for _, key := range sortKeys(val.MapKeys()) {
oneIteration(key, val.MapIndex(key))
}
return
case reflect.Chan:
if val.IsNil() {
break
}
i := 0
for ; ; i++ {
elem, ok := val.Recv()
if !ok {
break
}
oneIteration(reflect.ValueOf(i), elem)
}
if i == 0 {
break
}
return
case reflect.Invalid:
break // An invalid value is likely a nil map, etc. and acts like an empty map.
default:
s.errorf("range can't iterate over %v", val)
}
if r.ElseList != nil {
s.walk(dot, r.ElseList)
}
}
func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {
s.at(t)
tmpl := s.tmpl.tmpl[t.Name]
if tmpl == nil {
s.errorf("template %q not defined", t.Name)
}
// Variables declared by the pipeline persist.
dot = s.evalPipeline(dot, t.Pipe)
newState := *s
newState.tmpl = tmpl
// No dynamic scoping: template invocations inherit no variables.
newState.vars = []variable{{"$", dot}}
newState.walk(dot, tmpl.Root)
}
// Eval functions evaluate pipelines, commands, and their elements and extract
// values from the data structure by examining fields, calling methods, and so on.
// The printing of those values happens only through walk functions.
// evalPipeline returns the value acquired by evaluating a pipeline. If the
// pipeline has a variable declaration, the variable will be pushed on the
// stack. Callers should therefore pop the stack after they are finished
// executing commands depending on the pipeline value.
func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value reflect.Value) {
if pipe == nil {
return
}
s.at(pipe)
for _, cmd := range pipe.Cmds {
value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg.
// If the object has type interface{}, dig down one level to the thing inside.
if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 {
value = reflect.ValueOf(value.Interface()) // lovely!
}
}
for _, variable := range pipe.Decl {
s.push(variable.Ident[0], value)
}
return value
}
func (s *state) notAFunction(args []parse.Node, final reflect.Value) {
if len(args) > 1 || final.IsValid() {
s.errorf("can't give argument to non-function %s", args[0])
}
}
func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value {
firstWord := cmd.Args[0]
switch n := firstWord.(type) {
case *parse.FieldNode:
return s.evalFieldNode(dot, n, cmd.Args, final)
case *parse.ChainNode:
return s.evalChainNode(dot, n, cmd.Args, final)
case *parse.IdentifierNode:
// Must be a function.
return s.evalFunction(dot, n, cmd, cmd.Args, final)
case *parse.PipeNode:
// Parenthesized pipeline. The arguments are all inside the pipeline; final is ignored.
return s.evalPipeline(dot, n)
case *parse.VariableNode:
return s.evalVariableNode(dot, n, cmd.Args, final)
}
s.at(firstWord)
s.notAFunction(cmd.Args, final)
switch word := firstWord.(type) {
case *parse.BoolNode:
return reflect.ValueOf(word.True)
case *parse.DotNode:
return dot
case *parse.NilNode:
s.errorf("nil is not a command")
case *parse.NumberNode:
return s.idealConstant(word)
case *parse.StringNode:
return reflect.ValueOf(word.Text)
}
s.errorf("can't evaluate command %q", firstWord)
panic("not reached")
}
// idealConstant is called to return the value of a number in a context where
// we don't know the type. In that case, the syntax of the number tells us
// its type, and we use Go rules to resolve. Note there is no such thing as
// a uint ideal constant in this situation - the value must be of int type.
func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value {
// These are ideal constants but we don't know the type
// and we have no context. (If it was a method argument,
// we'd know what we need.) The syntax guides us to some extent.
s.at(constant)
switch {
case constant.IsComplex:
return reflect.ValueOf(constant.Complex128) // incontrovertible.
case constant.IsFloat && !isHexConstant(constant.Text) && strings.IndexAny(constant.Text, ".eE") >= 0:
return reflect.ValueOf(constant.Float64)
case constant.IsInt:
n := int(constant.Int64)
if int64(n) != constant.Int64 {
s.errorf("%s overflows int", constant.Text)
}
return reflect.ValueOf(n)
case constant.IsUint:
s.errorf("%s overflows int", constant.Text)
}
return zero
}
func isHexConstant(s string) bool {
return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')
}
func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value {
s.at(field)
return s.evalFieldChain(dot, dot, field, field.Ident, args, final)
}
func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value {
s.at(chain)
// (pipe).Field1.Field2 has pipe as .Node, fields as .Field. Eval the pipeline, then the fields.
pipe := s.evalArg(dot, nil, chain.Node)
if len(chain.Field) == 0 {
s.errorf("internal error: no fields in evalChainNode")
}
return s.evalFieldChain(dot, pipe, chain, chain.Field, args, final)
}
func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value {
// $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields.
s.at(variable)
value := s.varValue(variable.Ident[0])
if len(variable.Ident) == 1 {
s.notAFunction(args, final)
return value
}
return s.evalFieldChain(dot, value, variable, variable.Ident[1:], args, final)
}
// evalFieldChain evaluates .X.Y.Z possibly followed by arguments.
// dot is the environment in which to evaluate arguments, while
// receiver is the value being walked along the chain.
func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value {
n := len(ident)
for i := 0; i < n-1; i++ {
receiver = s.evalField(dot, ident[i], node, nil, zero, receiver)
}
// Now if it's a method, it gets the arguments.
return s.evalField(dot, ident[n-1], node, args, final, receiver)
}
func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value {
s.at(node)
name := node.Ident
function, ok := findFunction(name, s.tmpl)
if !ok {
s.errorf("%q is not a defined function", name)
}
return s.evalCall(dot, function, cmd, name, args, final)
}
// evalField evaluates an expression like (.Field) or (.Field arg1 arg2).
// The 'final' argument represents the return value from the preceding
// value of the pipeline, if any.
func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value {
if !receiver.IsValid() {
return zero
}
typ := receiver.Type()
receiver, _ = indirect(receiver)
// Unless it's an interface, need to get to a value of type *T to guarantee
// we see all methods of T and *T.
ptr := receiver
if ptr.Kind() != reflect.Interface && ptr.CanAddr() {
ptr = ptr.Addr()
}
if method := ptr.MethodByName(fieldName); method.IsValid() {
return s.evalCall(dot, method, node, fieldName, args, final)
}
hasArgs := len(args) > 1 || final.IsValid()
// It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil.
receiver, isNil := indirect(receiver)
if isNil {
s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
}
switch receiver.Kind() {
case reflect.Struct:
tField, ok := receiver.Type().FieldByName(fieldName)
if ok {
field := receiver.FieldByIndex(tField.Index)
if tField.PkgPath != "" { // field is unexported
s.errorf("%s is an unexported field of struct type %s", fieldName, typ)
}
// If it's a function, we must call it.
if hasArgs {
s.errorf("%s has arguments but cannot be invoked as function", fieldName)
}
return field
}
s.errorf("%s is not a field of struct type %s", fieldName, typ)
case reflect.Map:
// If it's a map, attempt to use the field name as a key.
nameVal := reflect.ValueOf(fieldName)
if nameVal.Type().AssignableTo(receiver.Type().Key()) {
if hasArgs {
s.errorf("%s is not a method but has arguments", fieldName)
}
return receiver.MapIndex(nameVal)
}
}
s.errorf("can't evaluate field %s in type %s", fieldName, typ)
panic("not reached")
}
var (
errorType = reflect.TypeOf((*error)(nil)).Elem()
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
)
// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]
// as the function itself.
func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value {
if args != nil {
args = args[1:] // Zeroth arg is function name/node; not passed to function.
}
typ := fun.Type()
numIn := len(args)
if final.IsValid() {
numIn++
}
numFixed := len(args)
if typ.IsVariadic() {
numFixed = typ.NumIn() - 1 // last arg is the variadic one.
if numIn < numFixed {
s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args))
}
} else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() {
s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args))
}
if !goodFunc(typ) {
// TODO: This could still be a confusing error; maybe goodFunc should provide info.
s.errorf("can't call method/function %q with %d results", name, typ.NumOut())
}
// Build the arg list.
argv := make([]reflect.Value, numIn)
// Args must be evaluated. Fixed args first.
i := 0
for ; i < numFixed && i < len(args); i++ {
argv[i] = s.evalArg(dot, typ.In(i), args[i])
}
// Now the ... args.
if typ.IsVariadic() {
argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice.
for ; i < len(args); i++ {
argv[i] = s.evalArg(dot, argType, args[i])
}
}
// Add final value if necessary.
if final.IsValid() {
t := typ.In(typ.NumIn() - 1)
if typ.IsVariadic() {
t = t.Elem()
}
argv[i] = s.validateType(final, t)
}
result := fun.Call(argv)
// If we have an error that is not nil, stop execution and return that error to the caller.
if len(result) == 2 && !result[1].IsNil() {
s.at(node)
s.errorf("error calling %s: %s", name, result[1].Interface().(error))
}
return result[0]
}
// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
func canBeNil(typ reflect.Type) bool {
switch typ.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return true
}
return false
}
// validateType guarantees that the value is valid and assignable to the type.
func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value {
if !value.IsValid() {
if typ == nil || canBeNil(typ) {
// An untyped nil interface{}. Accept as a proper nil value.
return reflect.Zero(typ)
}
s.errorf("invalid value; expected %s", typ)
}
if typ != nil && !value.Type().AssignableTo(typ) {
if value.Kind() == reflect.Interface && !value.IsNil() {
value = value.Elem()
if value.Type().AssignableTo(typ) {
return value
}
// fallthrough
}
// Does one dereference or indirection work? We could do more, as we
// do with method receivers, but that gets messy and method receivers
// are much more constrained, so it makes more sense there than here.
// Besides, one is almost always all you need.
switch {
case value.Kind() == reflect.Ptr && value.Type().Elem().AssignableTo(typ):
value = value.Elem()
if !value.IsValid() {
s.errorf("dereference of nil pointer of type %s", typ)
}
case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr():
value = value.Addr()
default:
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
}
}
return value
}
func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value {
s.at(n)
switch arg := n.(type) {
case *parse.DotNode:
return s.validateType(dot, typ)
case *parse.NilNode:
if canBeNil(typ) {
return reflect.Zero(typ)
}
s.errorf("cannot assign nil to %s", typ)
case *parse.FieldNode:
return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, zero), typ)
case *parse.VariableNode:
return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ)
case *parse.PipeNode:
return s.validateType(s.evalPipeline(dot, arg), typ)
case *parse.IdentifierNode:
return s.evalFunction(dot, arg, arg, nil, zero)
case *parse.ChainNode:
return s.validateType(s.evalChainNode(dot, arg, nil, zero), typ)
}
switch typ.Kind() {
case reflect.Bool:
return s.evalBool(typ, n)
case reflect.Complex64, reflect.Complex128:
return s.evalComplex(typ, n)
case reflect.Float32, reflect.Float64:
return s.evalFloat(typ, n)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return s.evalInteger(typ, n)
case reflect.Interface:
if typ.NumMethod() == 0 {
return s.evalEmptyInterface(dot, n)
}
case reflect.String:
return s.evalString(typ, n)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return s.evalUnsignedInteger(typ, n)
}
s.errorf("can't handle %s for arg of type %s", n, typ)
panic("not reached")
}
func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value {
s.at(n)
if n, ok := n.(*parse.BoolNode); ok {
value := reflect.New(typ).Elem()
value.SetBool(n.True)
return value
}
s.errorf("expected bool; found %s", n)
panic("not reached")
}
func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value {
s.at(n)
if n, ok := n.(*parse.StringNode); ok {
value := reflect.New(typ).Elem()
value.SetString(n.Text)
return value
}
s.errorf("expected string; found %s", n)
panic("not reached")
}
func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value {
s.at(n)
if n, ok := n.(*parse.NumberNode); ok && n.IsInt {
value := reflect.New(typ).Elem()
value.SetInt(n.Int64)
return value
}
s.errorf("expected integer; found %s", n)
panic("not reached")
}
func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value {
s.at(n)
if n, ok := n.(*parse.NumberNode); ok && n.IsUint {
value := reflect.New(typ).Elem()
value.SetUint(n.Uint64)
return value
}
s.errorf("expected unsigned integer; found %s", n)
panic("not reached")
}
func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value {
s.at(n)
if n, ok := n.(*parse.NumberNode); ok && n.IsFloat {
value := reflect.New(typ).Elem()
value.SetFloat(n.Float64)
return value
}
s.errorf("expected float; found %s", n)
panic("not reached")
}
func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value {
if n, ok := n.(*parse.NumberNode); ok && n.IsComplex {
value := reflect.New(typ).Elem()
value.SetComplex(n.Complex128)
return value
}
s.errorf("expected complex; found %s", n)
panic("not reached")
}
func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value {
s.at(n)
switch n := n.(type) {
case *parse.BoolNode:
return reflect.ValueOf(n.True)
case *parse.DotNode:
return dot
case *parse.FieldNode:
return s.evalFieldNode(dot, n, nil, zero)
case *parse.IdentifierNode:
return s.evalFunction(dot, n, n, nil, zero)
case *parse.NilNode:
// NilNode is handled in evalArg, the only place that calls here.
s.errorf("evalEmptyInterface: nil (can't happen)")
case *parse.NumberNode:
return s.idealConstant(n)
case *parse.StringNode:
return reflect.ValueOf(n.Text)
case *parse.VariableNode:
return s.evalVariableNode(dot, n, nil, zero)
case *parse.PipeNode:
return s.evalPipeline(dot, n)
}
s.errorf("can't handle assignment of %s to empty interface argument", n)
panic("not reached")
}
// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
// We indirect through pointers and empty interfaces (only) because
// non-empty interfaces have methods we might need.
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
if v.IsNil() {
return v, true
}
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
break
}
}
return v, false
}
// printValue writes the textual representation of the value to the output of
// the template.
func (s *state) printValue(n parse.Node, v reflect.Value) {
s.at(n)
iface, ok := printableValue(v)
if !ok {
s.errorf("can't print %s of type %s", n, v.Type())
}
fmt.Fprint(s.wr, iface)
}
// printableValue returns the, possibly indirected, interface value inside v that
// is best for a call to formatted printer.
func printableValue(v reflect.Value) (interface{}, bool) {
if v.Kind() == reflect.Ptr {
v, _ = indirect(v) // fmt.Fprint handles nil.
}
if !v.IsValid() {
return "<no value>", true
}
if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) {
if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) {
v = v.Addr()
} else {
switch v.Kind() {
case reflect.Chan, reflect.Func:
return nil, false
}
}
}
return v.Interface(), true
}
// Types to help sort the keys in a map for reproducible output.
type rvs []reflect.Value
func (x rvs) Len() int { return len(x) }
func (x rvs) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
type rvInts struct{ rvs }
func (x rvInts) Less(i, j int) bool { return x.rvs[i].Int() < x.rvs[j].Int() }
type rvUints struct{ rvs }
func (x rvUints) Less(i, j int) bool { return x.rvs[i].Uint() < x.rvs[j].Uint() }
type rvFloats struct{ rvs }
func (x rvFloats) Less(i, j int) bool { return x.rvs[i].Float() < x.rvs[j].Float() }
type rvStrings struct{ rvs }
func (x rvStrings) Less(i, j int) bool { return x.rvs[i].String() < x.rvs[j].String() }
// sortKeys sorts (if it can) the slice of reflect.Values, which is a slice of map keys.
func sortKeys(v []reflect.Value) []reflect.Value {
if len(v) <= 1 {
return v
}
switch v[0].Kind() {
case reflect.Float32, reflect.Float64:
sort.Sort(rvFloats{v})
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
sort.Sort(rvInts{v})
case reflect.String:
sort.Sort(rvStrings{v})
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
sort.Sort(rvUints{v})
}
return v
}

598
vendor/github.com/alecthomas/template/funcs.go generated vendored Normal file
View File

@@ -0,0 +1,598 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package template
import (
"bytes"
"errors"
"fmt"
"io"
"net/url"
"reflect"
"strings"
"unicode"
"unicode/utf8"
)
// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of
// which the second has type error. In that case, if the second (error)
// return value evaluates to non-nil during execution, execution terminates and
// Execute returns that error.
type FuncMap map[string]interface{}
var builtins = FuncMap{
"and": and,
"call": call,
"html": HTMLEscaper,
"index": index,
"js": JSEscaper,
"len": length,
"not": not,
"or": or,
"print": fmt.Sprint,
"printf": fmt.Sprintf,
"println": fmt.Sprintln,
"urlquery": URLQueryEscaper,
// Comparisons
"eq": eq, // ==
"ge": ge, // >=
"gt": gt, // >
"le": le, // <=
"lt": lt, // <
"ne": ne, // !=
}
var builtinFuncs = createValueFuncs(builtins)
// createValueFuncs turns a FuncMap into a map[string]reflect.Value
func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
m := make(map[string]reflect.Value)
addValueFuncs(m, funcMap)
return m
}
// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values.
func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
for name, fn := range in {
v := reflect.ValueOf(fn)
if v.Kind() != reflect.Func {
panic("value for " + name + " not a function")
}
if !goodFunc(v.Type()) {
panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut()))
}
out[name] = v
}
}
// addFuncs adds to values the functions in funcs. It does no checking of the input -
// call addValueFuncs first.
func addFuncs(out, in FuncMap) {
for name, fn := range in {
out[name] = fn
}
}
// goodFunc checks that the function or method has the right result signature.
func goodFunc(typ reflect.Type) bool {
// We allow functions with 1 result or 2 results where the second is an error.
switch {
case typ.NumOut() == 1:
return true
case typ.NumOut() == 2 && typ.Out(1) == errorType:
return true
}
return false
}
// findFunction looks for a function in the template, and global map.
func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
if tmpl != nil && tmpl.common != nil {
if fn := tmpl.execFuncs[name]; fn.IsValid() {
return fn, true
}
}
if fn := builtinFuncs[name]; fn.IsValid() {
return fn, true
}
return reflect.Value{}, false
}
// Indexing.
// index returns the result of indexing its first argument by the following
// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
// indexed item must be a map, slice, or array.
func index(item interface{}, indices ...interface{}) (interface{}, error) {
v := reflect.ValueOf(item)
for _, i := range indices {
index := reflect.ValueOf(i)
var isNil bool
if v, isNil = indirect(v); isNil {
return nil, fmt.Errorf("index of nil pointer")
}
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.String:
var x int64
switch index.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
x = index.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
x = int64(index.Uint())
default:
return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
}
if x < 0 || x >= int64(v.Len()) {
return nil, fmt.Errorf("index out of range: %d", x)
}
v = v.Index(int(x))
case reflect.Map:
if !index.IsValid() {
index = reflect.Zero(v.Type().Key())
}
if !index.Type().AssignableTo(v.Type().Key()) {
return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
}
if x := v.MapIndex(index); x.IsValid() {
v = x
} else {
v = reflect.Zero(v.Type().Elem())
}
default:
return nil, fmt.Errorf("can't index item of type %s", v.Type())
}
}
return v.Interface(), nil
}
// Length
// length returns the length of the item, with an error if it has no defined length.
func length(item interface{}) (int, error) {
v, isNil := indirect(reflect.ValueOf(item))
if isNil {
return 0, fmt.Errorf("len of nil pointer")
}
switch v.Kind() {
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
return v.Len(), nil
}
return 0, fmt.Errorf("len of type %s", v.Type())
}
// Function invocation
// call returns the result of evaluating the first argument as a function.
// The function must return 1 result, or 2 results, the second of which is an error.
func call(fn interface{}, args ...interface{}) (interface{}, error) {
v := reflect.ValueOf(fn)
typ := v.Type()
if typ.Kind() != reflect.Func {
return nil, fmt.Errorf("non-function of type %s", typ)
}
if !goodFunc(typ) {
return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
}
numIn := typ.NumIn()
var dddType reflect.Type
if typ.IsVariadic() {
if len(args) < numIn-1 {
return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
}
dddType = typ.In(numIn - 1).Elem()
} else {
if len(args) != numIn {
return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
}
}
argv := make([]reflect.Value, len(args))
for i, arg := range args {
value := reflect.ValueOf(arg)
// Compute the expected type. Clumsy because of variadics.
var argType reflect.Type
if !typ.IsVariadic() || i < numIn-1 {
argType = typ.In(i)
} else {
argType = dddType
}
if !value.IsValid() && canBeNil(argType) {
value = reflect.Zero(argType)
}
if !value.Type().AssignableTo(argType) {
return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
}
argv[i] = value
}
result := v.Call(argv)
if len(result) == 2 && !result[1].IsNil() {
return result[0].Interface(), result[1].Interface().(error)
}
return result[0].Interface(), nil
}
// Boolean logic.
func truth(a interface{}) bool {
t, _ := isTrue(reflect.ValueOf(a))
return t
}
// and computes the Boolean AND of its arguments, returning
// the first false argument it encounters, or the last argument.
func and(arg0 interface{}, args ...interface{}) interface{} {
if !truth(arg0) {
return arg0
}
for i := range args {
arg0 = args[i]
if !truth(arg0) {
break
}
}
return arg0
}
// or computes the Boolean OR of its arguments, returning
// the first true argument it encounters, or the last argument.
func or(arg0 interface{}, args ...interface{}) interface{} {
if truth(arg0) {
return arg0
}
for i := range args {
arg0 = args[i]
if truth(arg0) {
break
}
}
return arg0
}
// not returns the Boolean negation of its argument.
func not(arg interface{}) (truth bool) {
truth, _ = isTrue(reflect.ValueOf(arg))
return !truth
}
// Comparison.
// TODO: Perhaps allow comparison between signed and unsigned integers.
var (
errBadComparisonType = errors.New("invalid type for comparison")
errBadComparison = errors.New("incompatible types for comparison")
errNoComparison = errors.New("missing argument for comparison")
)
type kind int
const (
invalidKind kind = iota
boolKind
complexKind
intKind
floatKind
integerKind
stringKind
uintKind
)
func basicKind(v reflect.Value) (kind, error) {
switch v.Kind() {
case reflect.Bool:
return boolKind, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return intKind, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return uintKind, nil
case reflect.Float32, reflect.Float64:
return floatKind, nil
case reflect.Complex64, reflect.Complex128:
return complexKind, nil
case reflect.String:
return stringKind, nil
}
return invalidKind, errBadComparisonType
}
// eq evaluates the comparison a == b || a == c || ...
func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
v1 := reflect.ValueOf(arg1)
k1, err := basicKind(v1)
if err != nil {
return false, err
}
if len(arg2) == 0 {
return false, errNoComparison
}
for _, arg := range arg2 {
v2 := reflect.ValueOf(arg)
k2, err := basicKind(v2)
if err != nil {
return false, err
}
truth := false
if k1 != k2 {
// Special case: Can compare integer values regardless of type's sign.
switch {
case k1 == intKind && k2 == uintKind:
truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint()
case k1 == uintKind && k2 == intKind:
truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int())
default:
return false, errBadComparison
}
} else {
switch k1 {
case boolKind:
truth = v1.Bool() == v2.Bool()
case complexKind:
truth = v1.Complex() == v2.Complex()
case floatKind:
truth = v1.Float() == v2.Float()
case intKind:
truth = v1.Int() == v2.Int()
case stringKind:
truth = v1.String() == v2.String()
case uintKind:
truth = v1.Uint() == v2.Uint()
default:
panic("invalid kind")
}
}
if truth {
return true, nil
}
}
return false, nil
}
// ne evaluates the comparison a != b.
func ne(arg1, arg2 interface{}) (bool, error) {
// != is the inverse of ==.
equal, err := eq(arg1, arg2)
return !equal, err
}
// lt evaluates the comparison a < b.
func lt(arg1, arg2 interface{}) (bool, error) {
v1 := reflect.ValueOf(arg1)
k1, err := basicKind(v1)
if err != nil {
return false, err
}
v2 := reflect.ValueOf(arg2)
k2, err := basicKind(v2)
if err != nil {
return false, err
}
truth := false
if k1 != k2 {
// Special case: Can compare integer values regardless of type's sign.
switch {
case k1 == intKind && k2 == uintKind:
truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint()
case k1 == uintKind && k2 == intKind:
truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int())
default:
return false, errBadComparison
}
} else {
switch k1 {
case boolKind, complexKind:
return false, errBadComparisonType
case floatKind:
truth = v1.Float() < v2.Float()
case intKind:
truth = v1.Int() < v2.Int()
case stringKind:
truth = v1.String() < v2.String()
case uintKind:
truth = v1.Uint() < v2.Uint()
default:
panic("invalid kind")
}
}
return truth, nil
}
// le evaluates the comparison <= b.
func le(arg1, arg2 interface{}) (bool, error) {
// <= is < or ==.
lessThan, err := lt(arg1, arg2)
if lessThan || err != nil {
return lessThan, err
}
return eq(arg1, arg2)
}
// gt evaluates the comparison a > b.
func gt(arg1, arg2 interface{}) (bool, error) {
// > is the inverse of <=.
lessOrEqual, err := le(arg1, arg2)
if err != nil {
return false, err
}
return !lessOrEqual, nil
}
// ge evaluates the comparison a >= b.
func ge(arg1, arg2 interface{}) (bool, error) {
// >= is the inverse of <.
lessThan, err := lt(arg1, arg2)
if err != nil {
return false, err
}
return !lessThan, nil
}
// HTML escaping.
var (
htmlQuot = []byte("&#34;") // shorter than "&quot;"
htmlApos = []byte("&#39;") // shorter than "&apos;" and apos was not in HTML until HTML5
htmlAmp = []byte("&amp;")
htmlLt = []byte("&lt;")
htmlGt = []byte("&gt;")
)
// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
func HTMLEscape(w io.Writer, b []byte) {
last := 0
for i, c := range b {
var html []byte
switch c {
case '"':
html = htmlQuot
case '\'':
html = htmlApos
case '&':
html = htmlAmp
case '<':
html = htmlLt
case '>':
html = htmlGt
default:
continue
}
w.Write(b[last:i])
w.Write(html)
last = i + 1
}
w.Write(b[last:])
}
// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
func HTMLEscapeString(s string) string {
// Avoid allocation if we can.
if strings.IndexAny(s, `'"&<>`) < 0 {
return s
}
var b bytes.Buffer
HTMLEscape(&b, []byte(s))
return b.String()
}
// HTMLEscaper returns the escaped HTML equivalent of the textual
// representation of its arguments.
func HTMLEscaper(args ...interface{}) string {
return HTMLEscapeString(evalArgs(args))
}
// JavaScript escaping.
var (
jsLowUni = []byte(`\u00`)
hex = []byte("0123456789ABCDEF")
jsBackslash = []byte(`\\`)
jsApos = []byte(`\'`)
jsQuot = []byte(`\"`)
jsLt = []byte(`\x3C`)
jsGt = []byte(`\x3E`)
)
// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
func JSEscape(w io.Writer, b []byte) {
last := 0
for i := 0; i < len(b); i++ {
c := b[i]
if !jsIsSpecial(rune(c)) {
// fast path: nothing to do
continue
}
w.Write(b[last:i])
if c < utf8.RuneSelf {
// Quotes, slashes and angle brackets get quoted.
// Control characters get written as \u00XX.
switch c {
case '\\':
w.Write(jsBackslash)
case '\'':
w.Write(jsApos)
case '"':
w.Write(jsQuot)
case '<':
w.Write(jsLt)
case '>':
w.Write(jsGt)
default:
w.Write(jsLowUni)
t, b := c>>4, c&0x0f
w.Write(hex[t : t+1])
w.Write(hex[b : b+1])
}
} else {
// Unicode rune.
r, size := utf8.DecodeRune(b[i:])
if unicode.IsPrint(r) {
w.Write(b[i : i+size])
} else {
fmt.Fprintf(w, "\\u%04X", r)
}
i += size - 1
}
last = i + 1
}
w.Write(b[last:])
}
// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
func JSEscapeString(s string) string {
// Avoid allocation if we can.
if strings.IndexFunc(s, jsIsSpecial) < 0 {
return s
}
var b bytes.Buffer
JSEscape(&b, []byte(s))
return b.String()
}
func jsIsSpecial(r rune) bool {
switch r {
case '\\', '\'', '"', '<', '>':
return true
}
return r < ' ' || utf8.RuneSelf <= r
}
// JSEscaper returns the escaped JavaScript equivalent of the textual
// representation of its arguments.
func JSEscaper(args ...interface{}) string {
return JSEscapeString(evalArgs(args))
}
// URLQueryEscaper returns the escaped value of the textual representation of
// its arguments in a form suitable for embedding in a URL query.
func URLQueryEscaper(args ...interface{}) string {
return url.QueryEscape(evalArgs(args))
}
// evalArgs formats the list of arguments into a string. It is therefore equivalent to
// fmt.Sprint(args...)
// except that each argument is indirected (if a pointer), as required,
// using the same rules as the default string evaluation during template
// execution.
func evalArgs(args []interface{}) string {
ok := false
var s string
// Fast path for simple common case.
if len(args) == 1 {
s, ok = args[0].(string)
}
if !ok {
for i, arg := range args {
a, ok := printableValue(reflect.ValueOf(arg))
if ok {
args[i] = a
} // else left fmt do its thing
}
s = fmt.Sprint(args...)
}
return s
}

108
vendor/github.com/alecthomas/template/helper.go generated vendored Normal file
View File

@@ -0,0 +1,108 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Helper functions to make constructing templates easier.
package template
import (
"fmt"
"io/ioutil"
"path/filepath"
)
// Functions and methods to parse templates.
// Must is a helper that wraps a call to a function returning (*Template, error)
// and panics if the error is non-nil. It is intended for use in variable
// initializations such as
// var t = template.Must(template.New("name").Parse("text"))
func Must(t *Template, err error) *Template {
if err != nil {
panic(err)
}
return t
}
// ParseFiles creates a new Template and parses the template definitions from
// the named files. The returned template's name will have the (base) name and
// (parsed) contents of the first file. There must be at least one file.
// If an error occurs, parsing stops and the returned *Template is nil.
func ParseFiles(filenames ...string) (*Template, error) {
return parseFiles(nil, filenames...)
}
// ParseFiles parses the named files and associates the resulting templates with
// t. If an error occurs, parsing stops and the returned template is nil;
// otherwise it is t. There must be at least one file.
func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
return parseFiles(t, filenames...)
}
// parseFiles is the helper for the method and function. If the argument
// template is nil, it is created from the first file.
func parseFiles(t *Template, filenames ...string) (*Template, error) {
if len(filenames) == 0 {
// Not really a problem, but be consistent.
return nil, fmt.Errorf("template: no files named in call to ParseFiles")
}
for _, filename := range filenames {
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
s := string(b)
name := filepath.Base(filename)
// First template becomes return value if not already defined,
// and we use that one for subsequent New calls to associate
// all the templates together. Also, if this file has the same name
// as t, this file becomes the contents of t, so
// t, err := New(name).Funcs(xxx).ParseFiles(name)
// works. Otherwise we create a new template associated with t.
var tmpl *Template
if t == nil {
t = New(name)
}
if name == t.Name() {
tmpl = t
} else {
tmpl = t.New(name)
}
_, err = tmpl.Parse(s)
if err != nil {
return nil, err
}
}
return t, nil
}
// ParseGlob creates a new Template and parses the template definitions from the
// files identified by the pattern, which must match at least one file. The
// returned template will have the (base) name and (parsed) contents of the
// first file matched by the pattern. ParseGlob is equivalent to calling
// ParseFiles with the list of files matched by the pattern.
func ParseGlob(pattern string) (*Template, error) {
return parseGlob(nil, pattern)
}
// ParseGlob parses the template definitions in the files identified by the
// pattern and associates the resulting templates with t. The pattern is
// processed by filepath.Glob and must match at least one file. ParseGlob is
// equivalent to calling t.ParseFiles with the list of files matched by the
// pattern.
func (t *Template) ParseGlob(pattern string) (*Template, error) {
return parseGlob(t, pattern)
}
// parseGlob is the implementation of the function and method ParseGlob.
func parseGlob(t *Template, pattern string) (*Template, error) {
filenames, err := filepath.Glob(pattern)
if err != nil {
return nil, err
}
if len(filenames) == 0 {
return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
}
return parseFiles(t, filenames...)
}

556
vendor/github.com/alecthomas/template/parse/lex.go generated vendored Normal file
View File

@@ -0,0 +1,556 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package parse
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
)
// item represents a token or text string returned from the scanner.
type item struct {
typ itemType // The type of this item.
pos Pos // The starting position, in bytes, of this item in the input string.
val string // The value of this item.
}
func (i item) String() string {
switch {
case i.typ == itemEOF:
return "EOF"
case i.typ == itemError:
return i.val
case i.typ > itemKeyword:
return fmt.Sprintf("<%s>", i.val)
case len(i.val) > 10:
return fmt.Sprintf("%.10q...", i.val)
}
return fmt.Sprintf("%q", i.val)
}
// itemType identifies the type of lex items.
type itemType int
const (
itemError itemType = iota // error occurred; value is text of error
itemBool // boolean constant
itemChar // printable ASCII character; grab bag for comma etc.
itemCharConstant // character constant
itemComplex // complex constant (1+2i); imaginary is just a number
itemColonEquals // colon-equals (':=') introducing a declaration
itemEOF
itemField // alphanumeric identifier starting with '.'
itemIdentifier // alphanumeric identifier not starting with '.'
itemLeftDelim // left action delimiter
itemLeftParen // '(' inside action
itemNumber // simple number, including imaginary
itemPipe // pipe symbol
itemRawString // raw quoted string (includes quotes)
itemRightDelim // right action delimiter
itemElideNewline // elide newline after right delim
itemRightParen // ')' inside action
itemSpace // run of spaces separating arguments
itemString // quoted string (includes quotes)
itemText // plain text
itemVariable // variable starting with '$', such as '$' or '$1' or '$hello'
// Keywords appear after all the rest.
itemKeyword // used only to delimit the keywords
itemDot // the cursor, spelled '.'
itemDefine // define keyword
itemElse // else keyword
itemEnd // end keyword
itemIf // if keyword
itemNil // the untyped nil constant, easiest to treat as a keyword
itemRange // range keyword
itemTemplate // template keyword
itemWith // with keyword
)
var key = map[string]itemType{
".": itemDot,
"define": itemDefine,
"else": itemElse,
"end": itemEnd,
"if": itemIf,
"range": itemRange,
"nil": itemNil,
"template": itemTemplate,
"with": itemWith,
}
const eof = -1
// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*lexer) stateFn
// lexer holds the state of the scanner.
type lexer struct {
name string // the name of the input; used only for error reports
input string // the string being scanned
leftDelim string // start of action
rightDelim string // end of action
state stateFn // the next lexing function to enter
pos Pos // current position in the input
start Pos // start position of this item
width Pos // width of last rune read from input
lastPos Pos // position of most recent item returned by nextItem
items chan item // channel of scanned items
parenDepth int // nesting depth of ( ) exprs
}
// next returns the next rune in the input.
func (l *lexer) next() rune {
if int(l.pos) >= len(l.input) {
l.width = 0
return eof
}
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
l.width = Pos(w)
l.pos += l.width
return r
}
// peek returns but does not consume the next rune in the input.
func (l *lexer) peek() rune {
r := l.next()
l.backup()
return r
}
// backup steps back one rune. Can only be called once per call of next.
func (l *lexer) backup() {
l.pos -= l.width
}
// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
l.items <- item{t, l.start, l.input[l.start:l.pos]}
l.start = l.pos
}
// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
l.start = l.pos
}
// accept consumes the next rune if it's from the valid set.
func (l *lexer) accept(valid string) bool {
if strings.IndexRune(valid, l.next()) >= 0 {
return true
}
l.backup()
return false
}
// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
for strings.IndexRune(valid, l.next()) >= 0 {
}
l.backup()
}
// lineNumber reports which line we're on, based on the position of
// the previous item returned by nextItem. Doing it this way
// means we don't have to worry about peek double counting.
func (l *lexer) lineNumber() int {
return 1 + strings.Count(l.input[:l.lastPos], "\n")
}
// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
return nil
}
// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
item := <-l.items
l.lastPos = item.pos
return item
}
// lex creates a new scanner for the input string.
func lex(name, input, left, right string) *lexer {
if left == "" {
left = leftDelim
}
if right == "" {
right = rightDelim
}
l := &lexer{
name: name,
input: input,
leftDelim: left,
rightDelim: right,
items: make(chan item),
}
go l.run()
return l
}
// run runs the state machine for the lexer.
func (l *lexer) run() {
for l.state = lexText; l.state != nil; {
l.state = l.state(l)
}
}
// state functions
const (
leftDelim = "{{"
rightDelim = "}}"
leftComment = "/*"
rightComment = "*/"
)
// lexText scans until an opening action delimiter, "{{".
func lexText(l *lexer) stateFn {
for {
if strings.HasPrefix(l.input[l.pos:], l.leftDelim) {
if l.pos > l.start {
l.emit(itemText)
}
return lexLeftDelim
}
if l.next() == eof {
break
}
}
// Correctly reached EOF.
if l.pos > l.start {
l.emit(itemText)
}
l.emit(itemEOF)
return nil
}
// lexLeftDelim scans the left delimiter, which is known to be present.
func lexLeftDelim(l *lexer) stateFn {
l.pos += Pos(len(l.leftDelim))
if strings.HasPrefix(l.input[l.pos:], leftComment) {
return lexComment
}
l.emit(itemLeftDelim)
l.parenDepth = 0
return lexInsideAction
}
// lexComment scans a comment. The left comment marker is known to be present.
func lexComment(l *lexer) stateFn {
l.pos += Pos(len(leftComment))
i := strings.Index(l.input[l.pos:], rightComment)
if i < 0 {
return l.errorf("unclosed comment")
}
l.pos += Pos(i + len(rightComment))
if !strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
return l.errorf("comment ends before closing delimiter")
}
l.pos += Pos(len(l.rightDelim))
l.ignore()
return lexText
}
// lexRightDelim scans the right delimiter, which is known to be present.
func lexRightDelim(l *lexer) stateFn {
l.pos += Pos(len(l.rightDelim))
l.emit(itemRightDelim)
if l.peek() == '\\' {
l.pos++
l.emit(itemElideNewline)
}
return lexText
}
// lexInsideAction scans the elements inside action delimiters.
func lexInsideAction(l *lexer) stateFn {
// Either number, quoted string, or identifier.
// Spaces separate arguments; runs of spaces turn into itemSpace.
// Pipe symbols separate and are emitted.
if strings.HasPrefix(l.input[l.pos:], l.rightDelim+"\\") || strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
if l.parenDepth == 0 {
return lexRightDelim
}
return l.errorf("unclosed left paren")
}
switch r := l.next(); {
case r == eof || isEndOfLine(r):
return l.errorf("unclosed action")
case isSpace(r):
return lexSpace
case r == ':':
if l.next() != '=' {
return l.errorf("expected :=")
}
l.emit(itemColonEquals)
case r == '|':
l.emit(itemPipe)
case r == '"':
return lexQuote
case r == '`':
return lexRawQuote
case r == '$':
return lexVariable
case r == '\'':
return lexChar
case r == '.':
// special look-ahead for ".field" so we don't break l.backup().
if l.pos < Pos(len(l.input)) {
r := l.input[l.pos]
if r < '0' || '9' < r {
return lexField
}
}
fallthrough // '.' can start a number.
case r == '+' || r == '-' || ('0' <= r && r <= '9'):
l.backup()
return lexNumber
case isAlphaNumeric(r):
l.backup()
return lexIdentifier
case r == '(':
l.emit(itemLeftParen)
l.parenDepth++
return lexInsideAction
case r == ')':
l.emit(itemRightParen)
l.parenDepth--
if l.parenDepth < 0 {
return l.errorf("unexpected right paren %#U", r)
}
return lexInsideAction
case r <= unicode.MaxASCII && unicode.IsPrint(r):
l.emit(itemChar)
return lexInsideAction
default:
return l.errorf("unrecognized character in action: %#U", r)
}
return lexInsideAction
}
// lexSpace scans a run of space characters.
// One space has already been seen.
func lexSpace(l *lexer) stateFn {
for isSpace(l.peek()) {
l.next()
}
l.emit(itemSpace)
return lexInsideAction
}
// lexIdentifier scans an alphanumeric.
func lexIdentifier(l *lexer) stateFn {
Loop:
for {
switch r := l.next(); {
case isAlphaNumeric(r):
// absorb.
default:
l.backup()
word := l.input[l.start:l.pos]
if !l.atTerminator() {
return l.errorf("bad character %#U", r)
}
switch {
case key[word] > itemKeyword:
l.emit(key[word])
case word[0] == '.':
l.emit(itemField)
case word == "true", word == "false":
l.emit(itemBool)
default:
l.emit(itemIdentifier)
}
break Loop
}
}
return lexInsideAction
}
// lexField scans a field: .Alphanumeric.
// The . has been scanned.
func lexField(l *lexer) stateFn {
return lexFieldOrVariable(l, itemField)
}
// lexVariable scans a Variable: $Alphanumeric.
// The $ has been scanned.
func lexVariable(l *lexer) stateFn {
if l.atTerminator() { // Nothing interesting follows -> "$".
l.emit(itemVariable)
return lexInsideAction
}
return lexFieldOrVariable(l, itemVariable)
}
// lexVariable scans a field or variable: [.$]Alphanumeric.
// The . or $ has been scanned.
func lexFieldOrVariable(l *lexer, typ itemType) stateFn {
if l.atTerminator() { // Nothing interesting follows -> "." or "$".
if typ == itemVariable {
l.emit(itemVariable)
} else {
l.emit(itemDot)
}
return lexInsideAction
}
var r rune
for {
r = l.next()
if !isAlphaNumeric(r) {
l.backup()
break
}
}
if !l.atTerminator() {
return l.errorf("bad character %#U", r)
}
l.emit(typ)
return lexInsideAction
}
// atTerminator reports whether the input is at valid termination character to
// appear after an identifier. Breaks .X.Y into two pieces. Also catches cases
// like "$x+2" not being acceptable without a space, in case we decide one
// day to implement arithmetic.
func (l *lexer) atTerminator() bool {
r := l.peek()
if isSpace(r) || isEndOfLine(r) {
return true
}
switch r {
case eof, '.', ',', '|', ':', ')', '(':
return true
}
// Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will
// succeed but should fail) but only in extremely rare cases caused by willfully
// bad choice of delimiter.
if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r {
return true
}
return false
}
// lexChar scans a character constant. The initial quote is already
// scanned. Syntax checking is done by the parser.
func lexChar(l *lexer) stateFn {
Loop:
for {
switch l.next() {
case '\\':
if r := l.next(); r != eof && r != '\n' {
break
}
fallthrough
case eof, '\n':
return l.errorf("unterminated character constant")
case '\'':
break Loop
}
}
l.emit(itemCharConstant)
return lexInsideAction
}
// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
// and "089" - but when it's wrong the input is invalid and the parser (via
// strconv) will notice.
func lexNumber(l *lexer) stateFn {
if !l.scanNumber() {
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
}
if sign := l.peek(); sign == '+' || sign == '-' {
// Complex: 1+2i. No spaces, must end in 'i'.
if !l.scanNumber() || l.input[l.pos-1] != 'i' {
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
}
l.emit(itemComplex)
} else {
l.emit(itemNumber)
}
return lexInsideAction
}
func (l *lexer) scanNumber() bool {
// Optional leading sign.
l.accept("+-")
// Is it hex?
digits := "0123456789"
if l.accept("0") && l.accept("xX") {
digits = "0123456789abcdefABCDEF"
}
l.acceptRun(digits)
if l.accept(".") {
l.acceptRun(digits)
}
if l.accept("eE") {
l.accept("+-")
l.acceptRun("0123456789")
}
// Is it imaginary?
l.accept("i")
// Next thing mustn't be alphanumeric.
if isAlphaNumeric(l.peek()) {
l.next()
return false
}
return true
}
// lexQuote scans a quoted string.
func lexQuote(l *lexer) stateFn {
Loop:
for {
switch l.next() {
case '\\':
if r := l.next(); r != eof && r != '\n' {
break
}
fallthrough
case eof, '\n':
return l.errorf("unterminated quoted string")
case '"':
break Loop
}
}
l.emit(itemString)
return lexInsideAction
}
// lexRawQuote scans a raw quoted string.
func lexRawQuote(l *lexer) stateFn {
Loop:
for {
switch l.next() {
case eof, '\n':
return l.errorf("unterminated raw quoted string")
case '`':
break Loop
}
}
l.emit(itemRawString)
return lexInsideAction
}
// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
return r == ' ' || r == '\t'
}
// isEndOfLine reports whether r is an end-of-line character.
func isEndOfLine(r rune) bool {
return r == '\r' || r == '\n'
}
// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
func isAlphaNumeric(r rune) bool {
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
}

834
vendor/github.com/alecthomas/template/parse/node.go generated vendored Normal file
View File

@@ -0,0 +1,834 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Parse nodes.
package parse
import (
"bytes"
"fmt"
"strconv"
"strings"
)
var textFormat = "%s" // Changed to "%q" in tests for better error messages.
// A Node is an element in the parse tree. The interface is trivial.
// The interface contains an unexported method so that only
// types local to this package can satisfy it.
type Node interface {
Type() NodeType
String() string
// Copy does a deep copy of the Node and all its components.
// To avoid type assertions, some XxxNodes also have specialized
// CopyXxx methods that return *XxxNode.
Copy() Node
Position() Pos // byte position of start of node in full original input string
// tree returns the containing *Tree.
// It is unexported so all implementations of Node are in this package.
tree() *Tree
}
// NodeType identifies the type of a parse tree node.
type NodeType int
// Pos represents a byte position in the original input text from which
// this template was parsed.
type Pos int
func (p Pos) Position() Pos {
return p
}
// Type returns itself and provides an easy default implementation
// for embedding in a Node. Embedded in all non-trivial Nodes.
func (t NodeType) Type() NodeType {
return t
}
const (
NodeText NodeType = iota // Plain text.
NodeAction // A non-control action such as a field evaluation.
NodeBool // A boolean constant.
NodeChain // A sequence of field accesses.
NodeCommand // An element of a pipeline.
NodeDot // The cursor, dot.
nodeElse // An else action. Not added to tree.
nodeEnd // An end action. Not added to tree.
NodeField // A field or method name.
NodeIdentifier // An identifier; always a function name.
NodeIf // An if action.
NodeList // A list of Nodes.
NodeNil // An untyped nil constant.
NodeNumber // A numerical constant.
NodePipe // A pipeline of commands.
NodeRange // A range action.
NodeString // A string constant.
NodeTemplate // A template invocation action.
NodeVariable // A $ variable.
NodeWith // A with action.
)
// Nodes.
// ListNode holds a sequence of nodes.
type ListNode struct {
NodeType
Pos
tr *Tree
Nodes []Node // The element nodes in lexical order.
}
func (t *Tree) newList(pos Pos) *ListNode {
return &ListNode{tr: t, NodeType: NodeList, Pos: pos}
}
func (l *ListNode) append(n Node) {
l.Nodes = append(l.Nodes, n)
}
func (l *ListNode) tree() *Tree {
return l.tr
}
func (l *ListNode) String() string {
b := new(bytes.Buffer)
for _, n := range l.Nodes {
fmt.Fprint(b, n)
}
return b.String()
}
func (l *ListNode) CopyList() *ListNode {
if l == nil {
return l
}
n := l.tr.newList(l.Pos)
for _, elem := range l.Nodes {
n.append(elem.Copy())
}
return n
}
func (l *ListNode) Copy() Node {
return l.CopyList()
}
// TextNode holds plain text.
type TextNode struct {
NodeType
Pos
tr *Tree
Text []byte // The text; may span newlines.
}
func (t *Tree) newText(pos Pos, text string) *TextNode {
return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)}
}
func (t *TextNode) String() string {
return fmt.Sprintf(textFormat, t.Text)
}
func (t *TextNode) tree() *Tree {
return t.tr
}
func (t *TextNode) Copy() Node {
return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)}
}
// PipeNode holds a pipeline with optional declaration
type PipeNode struct {
NodeType
Pos
tr *Tree
Line int // The line number in the input (deprecated; kept for compatibility)
Decl []*VariableNode // Variable declarations in lexical order.
Cmds []*CommandNode // The commands in lexical order.
}
func (t *Tree) newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode {
return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: decl}
}
func (p *PipeNode) append(command *CommandNode) {
p.Cmds = append(p.Cmds, command)
}
func (p *PipeNode) String() string {
s := ""
if len(p.Decl) > 0 {
for i, v := range p.Decl {
if i > 0 {
s += ", "
}
s += v.String()
}
s += " := "
}
for i, c := range p.Cmds {
if i > 0 {
s += " | "
}
s += c.String()
}
return s
}
func (p *PipeNode) tree() *Tree {
return p.tr
}
func (p *PipeNode) CopyPipe() *PipeNode {
if p == nil {
return p
}
var decl []*VariableNode
for _, d := range p.Decl {
decl = append(decl, d.Copy().(*VariableNode))
}
n := p.tr.newPipeline(p.Pos, p.Line, decl)
for _, c := range p.Cmds {
n.append(c.Copy().(*CommandNode))
}
return n
}
func (p *PipeNode) Copy() Node {
return p.CopyPipe()
}
// ActionNode holds an action (something bounded by delimiters).
// Control actions have their own nodes; ActionNode represents simple
// ones such as field evaluations and parenthesized pipelines.
type ActionNode struct {
NodeType
Pos
tr *Tree
Line int // The line number in the input (deprecated; kept for compatibility)
Pipe *PipeNode // The pipeline in the action.
}
func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe}
}
func (a *ActionNode) String() string {
return fmt.Sprintf("{{%s}}", a.Pipe)
}
func (a *ActionNode) tree() *Tree {
return a.tr
}
func (a *ActionNode) Copy() Node {
return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
}
// CommandNode holds a command (a pipeline inside an evaluating action).
type CommandNode struct {
NodeType
Pos
tr *Tree
Args []Node // Arguments in lexical order: Identifier, field, or constant.
}
func (t *Tree) newCommand(pos Pos) *CommandNode {
return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos}
}
func (c *CommandNode) append(arg Node) {
c.Args = append(c.Args, arg)
}
func (c *CommandNode) String() string {
s := ""
for i, arg := range c.Args {
if i > 0 {
s += " "
}
if arg, ok := arg.(*PipeNode); ok {
s += "(" + arg.String() + ")"
continue
}
s += arg.String()
}
return s
}
func (c *CommandNode) tree() *Tree {
return c.tr
}
func (c *CommandNode) Copy() Node {
if c == nil {
return c
}
n := c.tr.newCommand(c.Pos)
for _, c := range c.Args {
n.append(c.Copy())
}
return n
}
// IdentifierNode holds an identifier.
type IdentifierNode struct {
NodeType
Pos
tr *Tree
Ident string // The identifier's name.
}
// NewIdentifier returns a new IdentifierNode with the given identifier name.
func NewIdentifier(ident string) *IdentifierNode {
return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident}
}
// SetPos sets the position. NewIdentifier is a public method so we can't modify its signature.
// Chained for convenience.
// TODO: fix one day?
func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode {
i.Pos = pos
return i
}
// SetTree sets the parent tree for the node. NewIdentifier is a public method so we can't modify its signature.
// Chained for convenience.
// TODO: fix one day?
func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode {
i.tr = t
return i
}
func (i *IdentifierNode) String() string {
return i.Ident
}
func (i *IdentifierNode) tree() *Tree {
return i.tr
}
func (i *IdentifierNode) Copy() Node {
return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos)
}
// VariableNode holds a list of variable names, possibly with chained field
// accesses. The dollar sign is part of the (first) name.
type VariableNode struct {
NodeType
Pos
tr *Tree
Ident []string // Variable name and fields in lexical order.
}
func (t *Tree) newVariable(pos Pos, ident string) *VariableNode {
return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}
}
func (v *VariableNode) String() string {
s := ""
for i, id := range v.Ident {
if i > 0 {
s += "."
}
s += id
}
return s
}
func (v *VariableNode) tree() *Tree {
return v.tr
}
func (v *VariableNode) Copy() Node {
return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)}
}
// DotNode holds the special identifier '.'.
type DotNode struct {
NodeType
Pos
tr *Tree
}
func (t *Tree) newDot(pos Pos) *DotNode {
return &DotNode{tr: t, NodeType: NodeDot, Pos: pos}
}
func (d *DotNode) Type() NodeType {
// Override method on embedded NodeType for API compatibility.
// TODO: Not really a problem; could change API without effect but
// api tool complains.
return NodeDot
}
func (d *DotNode) String() string {
return "."
}
func (d *DotNode) tree() *Tree {
return d.tr
}
func (d *DotNode) Copy() Node {
return d.tr.newDot(d.Pos)
}
// NilNode holds the special identifier 'nil' representing an untyped nil constant.
type NilNode struct {
NodeType
Pos
tr *Tree
}
func (t *Tree) newNil(pos Pos) *NilNode {
return &NilNode{tr: t, NodeType: NodeNil, Pos: pos}
}
func (n *NilNode) Type() NodeType {
// Override method on embedded NodeType for API compatibility.
// TODO: Not really a problem; could change API without effect but
// api tool complains.
return NodeNil
}
func (n *NilNode) String() string {
return "nil"
}
func (n *NilNode) tree() *Tree {
return n.tr
}
func (n *NilNode) Copy() Node {
return n.tr.newNil(n.Pos)
}
// FieldNode holds a field (identifier starting with '.').
// The names may be chained ('.x.y').
// The period is dropped from each ident.
type FieldNode struct {
NodeType
Pos
tr *Tree
Ident []string // The identifiers in lexical order.
}
func (t *Tree) newField(pos Pos, ident string) *FieldNode {
return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
}
func (f *FieldNode) String() string {
s := ""
for _, id := range f.Ident {
s += "." + id
}
return s
}
func (f *FieldNode) tree() *Tree {
return f.tr
}
func (f *FieldNode) Copy() Node {
return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)}
}
// ChainNode holds a term followed by a chain of field accesses (identifier starting with '.').
// The names may be chained ('.x.y').
// The periods are dropped from each ident.
type ChainNode struct {
NodeType
Pos
tr *Tree
Node Node
Field []string // The identifiers in lexical order.
}
func (t *Tree) newChain(pos Pos, node Node) *ChainNode {
return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node}
}
// Add adds the named field (which should start with a period) to the end of the chain.
func (c *ChainNode) Add(field string) {
if len(field) == 0 || field[0] != '.' {
panic("no dot in field")
}
field = field[1:] // Remove leading dot.
if field == "" {
panic("empty field")
}
c.Field = append(c.Field, field)
}
func (c *ChainNode) String() string {
s := c.Node.String()
if _, ok := c.Node.(*PipeNode); ok {
s = "(" + s + ")"
}
for _, field := range c.Field {
s += "." + field
}
return s
}
func (c *ChainNode) tree() *Tree {
return c.tr
}
func (c *ChainNode) Copy() Node {
return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)}
}
// BoolNode holds a boolean constant.
type BoolNode struct {
NodeType
Pos
tr *Tree
True bool // The value of the boolean constant.
}
func (t *Tree) newBool(pos Pos, true bool) *BoolNode {
return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true}
}
func (b *BoolNode) String() string {
if b.True {
return "true"
}
return "false"
}
func (b *BoolNode) tree() *Tree {
return b.tr
}
func (b *BoolNode) Copy() Node {
return b.tr.newBool(b.Pos, b.True)
}
// NumberNode holds a number: signed or unsigned integer, float, or complex.
// The value is parsed and stored under all the types that can represent the value.
// This simulates in a small amount of code the behavior of Go's ideal constants.
type NumberNode struct {
NodeType
Pos
tr *Tree
IsInt bool // Number has an integral value.
IsUint bool // Number has an unsigned integral value.
IsFloat bool // Number has a floating-point value.
IsComplex bool // Number is complex.
Int64 int64 // The signed integer value.
Uint64 uint64 // The unsigned integer value.
Float64 float64 // The floating-point value.
Complex128 complex128 // The complex value.
Text string // The original textual representation from the input.
}
func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) {
n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text}
switch typ {
case itemCharConstant:
rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
if err != nil {
return nil, err
}
if tail != "'" {
return nil, fmt.Errorf("malformed character constant: %s", text)
}
n.Int64 = int64(rune)
n.IsInt = true
n.Uint64 = uint64(rune)
n.IsUint = true
n.Float64 = float64(rune) // odd but those are the rules.
n.IsFloat = true
return n, nil
case itemComplex:
// fmt.Sscan can parse the pair, so let it do the work.
if _, err := fmt.Sscan(text, &n.Complex128); err != nil {
return nil, err
}
n.IsComplex = true
n.simplifyComplex()
return n, nil
}
// Imaginary constants can only be complex unless they are zero.
if len(text) > 0 && text[len(text)-1] == 'i' {
f, err := strconv.ParseFloat(text[:len(text)-1], 64)
if err == nil {
n.IsComplex = true
n.Complex128 = complex(0, f)
n.simplifyComplex()
return n, nil
}
}
// Do integer test first so we get 0x123 etc.
u, err := strconv.ParseUint(text, 0, 64) // will fail for -0; fixed below.
if err == nil {
n.IsUint = true
n.Uint64 = u
}
i, err := strconv.ParseInt(text, 0, 64)
if err == nil {
n.IsInt = true
n.Int64 = i
if i == 0 {
n.IsUint = true // in case of -0.
n.Uint64 = u
}
}
// If an integer extraction succeeded, promote the float.
if n.IsInt {
n.IsFloat = true
n.Float64 = float64(n.Int64)
} else if n.IsUint {
n.IsFloat = true
n.Float64 = float64(n.Uint64)
} else {
f, err := strconv.ParseFloat(text, 64)
if err == nil {
n.IsFloat = true
n.Float64 = f
// If a floating-point extraction succeeded, extract the int if needed.
if !n.IsInt && float64(int64(f)) == f {
n.IsInt = true
n.Int64 = int64(f)
}
if !n.IsUint && float64(uint64(f)) == f {
n.IsUint = true
n.Uint64 = uint64(f)
}
}
}
if !n.IsInt && !n.IsUint && !n.IsFloat {
return nil, fmt.Errorf("illegal number syntax: %q", text)
}
return n, nil
}
// simplifyComplex pulls out any other types that are represented by the complex number.
// These all require that the imaginary part be zero.
func (n *NumberNode) simplifyComplex() {
n.IsFloat = imag(n.Complex128) == 0
if n.IsFloat {
n.Float64 = real(n.Complex128)
n.IsInt = float64(int64(n.Float64)) == n.Float64
if n.IsInt {
n.Int64 = int64(n.Float64)
}
n.IsUint = float64(uint64(n.Float64)) == n.Float64
if n.IsUint {
n.Uint64 = uint64(n.Float64)
}
}
}
func (n *NumberNode) String() string {
return n.Text
}
func (n *NumberNode) tree() *Tree {
return n.tr
}
func (n *NumberNode) Copy() Node {
nn := new(NumberNode)
*nn = *n // Easy, fast, correct.
return nn
}
// StringNode holds a string constant. The value has been "unquoted".
type StringNode struct {
NodeType
Pos
tr *Tree
Quoted string // The original text of the string, with quotes.
Text string // The string, after quote processing.
}
func (t *Tree) newString(pos Pos, orig, text string) *StringNode {
return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
}
func (s *StringNode) String() string {
return s.Quoted
}
func (s *StringNode) tree() *Tree {
return s.tr
}
func (s *StringNode) Copy() Node {
return s.tr.newString(s.Pos, s.Quoted, s.Text)
}
// endNode represents an {{end}} action.
// It does not appear in the final parse tree.
type endNode struct {
NodeType
Pos
tr *Tree
}
func (t *Tree) newEnd(pos Pos) *endNode {
return &endNode{tr: t, NodeType: nodeEnd, Pos: pos}
}
func (e *endNode) String() string {
return "{{end}}"
}
func (e *endNode) tree() *Tree {
return e.tr
}
func (e *endNode) Copy() Node {
return e.tr.newEnd(e.Pos)
}
// elseNode represents an {{else}} action. Does not appear in the final tree.
type elseNode struct {
NodeType
Pos
tr *Tree
Line int // The line number in the input (deprecated; kept for compatibility)
}
func (t *Tree) newElse(pos Pos, line int) *elseNode {
return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line}
}
func (e *elseNode) Type() NodeType {
return nodeElse
}
func (e *elseNode) String() string {
return "{{else}}"
}
func (e *elseNode) tree() *Tree {
return e.tr
}
func (e *elseNode) Copy() Node {
return e.tr.newElse(e.Pos, e.Line)
}
// BranchNode is the common representation of if, range, and with.
type BranchNode struct {
NodeType
Pos
tr *Tree
Line int // The line number in the input (deprecated; kept for compatibility)
Pipe *PipeNode // The pipeline to be evaluated.
List *ListNode // What to execute if the value is non-empty.
ElseList *ListNode // What to execute if the value is empty (nil if absent).
}
func (b *BranchNode) String() string {
name := ""
switch b.NodeType {
case NodeIf:
name = "if"
case NodeRange:
name = "range"
case NodeWith:
name = "with"
default:
panic("unknown branch type")
}
if b.ElseList != nil {
return fmt.Sprintf("{{%s %s}}%s{{else}}%s{{end}}", name, b.Pipe, b.List, b.ElseList)
}
return fmt.Sprintf("{{%s %s}}%s{{end}}", name, b.Pipe, b.List)
}
func (b *BranchNode) tree() *Tree {
return b.tr
}
func (b *BranchNode) Copy() Node {
switch b.NodeType {
case NodeIf:
return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
case NodeRange:
return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
case NodeWith:
return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
default:
panic("unknown branch type")
}
}
// IfNode represents an {{if}} action and its commands.
type IfNode struct {
BranchNode
}
func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
func (i *IfNode) Copy() Node {
return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
}
// RangeNode represents a {{range}} action and its commands.
type RangeNode struct {
BranchNode
}
func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
func (r *RangeNode) Copy() Node {
return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
}
// WithNode represents a {{with}} action and its commands.
type WithNode struct {
BranchNode
}
func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
func (w *WithNode) Copy() Node {
return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
}
// TemplateNode represents a {{template}} action.
type TemplateNode struct {
NodeType
Pos
tr *Tree
Line int // The line number in the input (deprecated; kept for compatibility)
Name string // The name of the template (unquoted).
Pipe *PipeNode // The command to evaluate as dot for the template.
}
func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode {
return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe}
}
func (t *TemplateNode) String() string {
if t.Pipe == nil {
return fmt.Sprintf("{{template %q}}", t.Name)
}
return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe)
}
func (t *TemplateNode) tree() *Tree {
return t.tr
}
func (t *TemplateNode) Copy() Node {
return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())
}

700
vendor/github.com/alecthomas/template/parse/parse.go generated vendored Normal file
View File

@@ -0,0 +1,700 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package parse builds parse trees for templates as defined by text/template
// and html/template. Clients should use those packages to construct templates
// rather than this one, which provides shared internal data structures not
// intended for general use.
package parse
import (
"bytes"
"fmt"
"runtime"
"strconv"
"strings"
)
// Tree is the representation of a single parsed template.
type Tree struct {
Name string // name of the template represented by the tree.
ParseName string // name of the top-level template during parsing, for error messages.
Root *ListNode // top-level root of the tree.
text string // text parsed to create the template (or its parent)
// Parsing only; cleared after parse.
funcs []map[string]interface{}
lex *lexer
token [3]item // three-token lookahead for parser.
peekCount int
vars []string // variables defined at the moment.
}
// Copy returns a copy of the Tree. Any parsing state is discarded.
func (t *Tree) Copy() *Tree {
if t == nil {
return nil
}
return &Tree{
Name: t.Name,
ParseName: t.ParseName,
Root: t.Root.CopyList(),
text: t.text,
}
}
// Parse returns a map from template name to parse.Tree, created by parsing the
// templates described in the argument string. The top-level template will be
// given the specified name. If an error is encountered, parsing stops and an
// empty map is returned with the error.
func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) {
treeSet = make(map[string]*Tree)
t := New(name)
t.text = text
_, err = t.Parse(text, leftDelim, rightDelim, treeSet, funcs...)
return
}
// next returns the next token.
func (t *Tree) next() item {
if t.peekCount > 0 {
t.peekCount--
} else {
t.token[0] = t.lex.nextItem()
}
return t.token[t.peekCount]
}
// backup backs the input stream up one token.
func (t *Tree) backup() {
t.peekCount++
}
// backup2 backs the input stream up two tokens.
// The zeroth token is already there.
func (t *Tree) backup2(t1 item) {
t.token[1] = t1
t.peekCount = 2
}
// backup3 backs the input stream up three tokens
// The zeroth token is already there.
func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back.
t.token[1] = t1
t.token[2] = t2
t.peekCount = 3
}
// peek returns but does not consume the next token.
func (t *Tree) peek() item {
if t.peekCount > 0 {
return t.token[t.peekCount-1]
}
t.peekCount = 1
t.token[0] = t.lex.nextItem()
return t.token[0]
}
// nextNonSpace returns the next non-space token.
func (t *Tree) nextNonSpace() (token item) {
for {
token = t.next()
if token.typ != itemSpace {
break
}
}
return token
}
// peekNonSpace returns but does not consume the next non-space token.
func (t *Tree) peekNonSpace() (token item) {
for {
token = t.next()
if token.typ != itemSpace {
break
}
}
t.backup()
return token
}
// Parsing.
// New allocates a new parse tree with the given name.
func New(name string, funcs ...map[string]interface{}) *Tree {
return &Tree{
Name: name,
funcs: funcs,
}
}
// ErrorContext returns a textual representation of the location of the node in the input text.
// The receiver is only used when the node does not have a pointer to the tree inside,
// which can occur in old code.
func (t *Tree) ErrorContext(n Node) (location, context string) {
pos := int(n.Position())
tree := n.tree()
if tree == nil {
tree = t
}
text := tree.text[:pos]
byteNum := strings.LastIndex(text, "\n")
if byteNum == -1 {
byteNum = pos // On first line.
} else {
byteNum++ // After the newline.
byteNum = pos - byteNum
}
lineNum := 1 + strings.Count(text, "\n")
context = n.String()
if len(context) > 20 {
context = fmt.Sprintf("%.20s...", context)
}
return fmt.Sprintf("%s:%d:%d", tree.ParseName, lineNum, byteNum), context
}
// errorf formats the error and terminates processing.
func (t *Tree) errorf(format string, args ...interface{}) {
t.Root = nil
format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
panic(fmt.Errorf(format, args...))
}
// error terminates processing.
func (t *Tree) error(err error) {
t.errorf("%s", err)
}
// expect consumes the next token and guarantees it has the required type.
func (t *Tree) expect(expected itemType, context string) item {
token := t.nextNonSpace()
if token.typ != expected {
t.unexpected(token, context)
}
return token
}
// expectOneOf consumes the next token and guarantees it has one of the required types.
func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
token := t.nextNonSpace()
if token.typ != expected1 && token.typ != expected2 {
t.unexpected(token, context)
}
return token
}
// unexpected complains about the token and terminates processing.
func (t *Tree) unexpected(token item, context string) {
t.errorf("unexpected %s in %s", token, context)
}
// recover is the handler that turns panics into returns from the top level of Parse.
func (t *Tree) recover(errp *error) {
e := recover()
if e != nil {
if _, ok := e.(runtime.Error); ok {
panic(e)
}
if t != nil {
t.stopParse()
}
*errp = e.(error)
}
return
}
// startParse initializes the parser, using the lexer.
func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) {
t.Root = nil
t.lex = lex
t.vars = []string{"$"}
t.funcs = funcs
}
// stopParse terminates parsing.
func (t *Tree) stopParse() {
t.lex = nil
t.vars = nil
t.funcs = nil
}
// Parse parses the template definition string to construct a representation of
// the template for execution. If either action delimiter string is empty, the
// default ("{{" or "}}") is used. Embedded template definitions are added to
// the treeSet map.
func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
defer t.recover(&err)
t.ParseName = t.Name
t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim))
t.text = text
t.parse(treeSet)
t.add(treeSet)
t.stopParse()
return t, nil
}
// add adds tree to the treeSet.
func (t *Tree) add(treeSet map[string]*Tree) {
tree := treeSet[t.Name]
if tree == nil || IsEmptyTree(tree.Root) {
treeSet[t.Name] = t
return
}
if !IsEmptyTree(t.Root) {
t.errorf("template: multiple definition of template %q", t.Name)
}
}
// IsEmptyTree reports whether this tree (node) is empty of everything but space.
func IsEmptyTree(n Node) bool {
switch n := n.(type) {
case nil:
return true
case *ActionNode:
case *IfNode:
case *ListNode:
for _, node := range n.Nodes {
if !IsEmptyTree(node) {
return false
}
}
return true
case *RangeNode:
case *TemplateNode:
case *TextNode:
return len(bytes.TrimSpace(n.Text)) == 0
case *WithNode:
default:
panic("unknown node: " + n.String())
}
return false
}
// parse is the top-level parser for a template, essentially the same
// as itemList except it also parses {{define}} actions.
// It runs to EOF.
func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
t.Root = t.newList(t.peek().pos)
for t.peek().typ != itemEOF {
if t.peek().typ == itemLeftDelim {
delim := t.next()
if t.nextNonSpace().typ == itemDefine {
newT := New("definition") // name will be updated once we know it.
newT.text = t.text
newT.ParseName = t.ParseName
newT.startParse(t.funcs, t.lex)
newT.parseDefinition(treeSet)
continue
}
t.backup2(delim)
}
n := t.textOrAction()
if n.Type() == nodeEnd {
t.errorf("unexpected %s", n)
}
t.Root.append(n)
}
return nil
}
// parseDefinition parses a {{define}} ... {{end}} template definition and
// installs the definition in the treeSet map. The "define" keyword has already
// been scanned.
func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
const context = "define clause"
name := t.expectOneOf(itemString, itemRawString, context)
var err error
t.Name, err = strconv.Unquote(name.val)
if err != nil {
t.error(err)
}
t.expect(itemRightDelim, context)
var end Node
t.Root, end = t.itemList()
if end.Type() != nodeEnd {
t.errorf("unexpected %s in %s", end, context)
}
t.add(treeSet)
t.stopParse()
}
// itemList:
// textOrAction*
// Terminates at {{end}} or {{else}}, returned separately.
func (t *Tree) itemList() (list *ListNode, next Node) {
list = t.newList(t.peekNonSpace().pos)
for t.peekNonSpace().typ != itemEOF {
n := t.textOrAction()
switch n.Type() {
case nodeEnd, nodeElse:
return list, n
}
list.append(n)
}
t.errorf("unexpected EOF")
return
}
// textOrAction:
// text | action
func (t *Tree) textOrAction() Node {
switch token := t.nextNonSpace(); token.typ {
case itemElideNewline:
return t.elideNewline()
case itemText:
return t.newText(token.pos, token.val)
case itemLeftDelim:
return t.action()
default:
t.unexpected(token, "input")
}
return nil
}
// elideNewline:
// Remove newlines trailing rightDelim if \\ is present.
func (t *Tree) elideNewline() Node {
token := t.peek()
if token.typ != itemText {
t.unexpected(token, "input")
return nil
}
t.next()
stripped := strings.TrimLeft(token.val, "\n\r")
diff := len(token.val) - len(stripped)
if diff > 0 {
// This is a bit nasty. We mutate the token in-place to remove
// preceding newlines.
token.pos += Pos(diff)
token.val = stripped
}
return t.newText(token.pos, token.val)
}
// Action:
// control
// command ("|" command)*
// Left delim is past. Now get actions.
// First word could be a keyword such as range.
func (t *Tree) action() (n Node) {
switch token := t.nextNonSpace(); token.typ {
case itemElse:
return t.elseControl()
case itemEnd:
return t.endControl()
case itemIf:
return t.ifControl()
case itemRange:
return t.rangeControl()
case itemTemplate:
return t.templateControl()
case itemWith:
return t.withControl()
}
t.backup()
// Do not pop variables; they persist until "end".
return t.newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))
}
// Pipeline:
// declarations? command ('|' command)*
func (t *Tree) pipeline(context string) (pipe *PipeNode) {
var decl []*VariableNode
pos := t.peekNonSpace().pos
// Are there declarations?
for {
if v := t.peekNonSpace(); v.typ == itemVariable {
t.next()
// Since space is a token, we need 3-token look-ahead here in the worst case:
// in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an
// argument variable rather than a declaration. So remember the token
// adjacent to the variable so we can push it back if necessary.
tokenAfterVariable := t.peek()
if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") {
t.nextNonSpace()
variable := t.newVariable(v.pos, v.val)
decl = append(decl, variable)
t.vars = append(t.vars, v.val)
if next.typ == itemChar && next.val == "," {
if context == "range" && len(decl) < 2 {
continue
}
t.errorf("too many declarations in %s", context)
}
} else if tokenAfterVariable.typ == itemSpace {
t.backup3(v, tokenAfterVariable)
} else {
t.backup2(v)
}
}
break
}
pipe = t.newPipeline(pos, t.lex.lineNumber(), decl)
for {
switch token := t.nextNonSpace(); token.typ {
case itemRightDelim, itemRightParen:
if len(pipe.Cmds) == 0 {
t.errorf("missing value for %s", context)
}
if token.typ == itemRightParen {
t.backup()
}
return
case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
t.backup()
pipe.append(t.command())
default:
t.unexpected(token, context)
}
}
}
func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
defer t.popVars(len(t.vars))
line = t.lex.lineNumber()
pipe = t.pipeline(context)
var next Node
list, next = t.itemList()
switch next.Type() {
case nodeEnd: //done
case nodeElse:
if allowElseIf {
// Special case for "else if". If the "else" is followed immediately by an "if",
// the elseControl will have left the "if" token pending. Treat
// {{if a}}_{{else if b}}_{{end}}
// as
// {{if a}}_{{else}}{{if b}}_{{end}}{{end}}.
// To do this, parse the if as usual and stop at it {{end}}; the subsequent{{end}}
// is assumed. This technique works even for long if-else-if chains.
// TODO: Should we allow else-if in with and range?
if t.peek().typ == itemIf {
t.next() // Consume the "if" token.
elseList = t.newList(next.Position())
elseList.append(t.ifControl())
// Do not consume the next item - only one {{end}} required.
break
}
}
elseList, next = t.itemList()
if next.Type() != nodeEnd {
t.errorf("expected end; found %s", next)
}
}
return pipe.Position(), line, pipe, list, elseList
}
// If:
// {{if pipeline}} itemList {{end}}
// {{if pipeline}} itemList {{else}} itemList {{end}}
// If keyword is past.
func (t *Tree) ifControl() Node {
return t.newIf(t.parseControl(true, "if"))
}
// Range:
// {{range pipeline}} itemList {{end}}
// {{range pipeline}} itemList {{else}} itemList {{end}}
// Range keyword is past.
func (t *Tree) rangeControl() Node {
return t.newRange(t.parseControl(false, "range"))
}
// With:
// {{with pipeline}} itemList {{end}}
// {{with pipeline}} itemList {{else}} itemList {{end}}
// If keyword is past.
func (t *Tree) withControl() Node {
return t.newWith(t.parseControl(false, "with"))
}
// End:
// {{end}}
// End keyword is past.
func (t *Tree) endControl() Node {
return t.newEnd(t.expect(itemRightDelim, "end").pos)
}
// Else:
// {{else}}
// Else keyword is past.
func (t *Tree) elseControl() Node {
// Special case for "else if".
peek := t.peekNonSpace()
if peek.typ == itemIf {
// We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ".
return t.newElse(peek.pos, t.lex.lineNumber())
}
return t.newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
}
// Template:
// {{template stringValue pipeline}}
// Template keyword is past. The name must be something that can evaluate
// to a string.
func (t *Tree) templateControl() Node {
var name string
token := t.nextNonSpace()
switch token.typ {
case itemString, itemRawString:
s, err := strconv.Unquote(token.val)
if err != nil {
t.error(err)
}
name = s
default:
t.unexpected(token, "template invocation")
}
var pipe *PipeNode
if t.nextNonSpace().typ != itemRightDelim {
t.backup()
// Do not pop variables; they persist until "end".
pipe = t.pipeline("template")
}
return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
}
// command:
// operand (space operand)*
// space-separated arguments up to a pipeline character or right delimiter.
// we consume the pipe character but leave the right delim to terminate the action.
func (t *Tree) command() *CommandNode {
cmd := t.newCommand(t.peekNonSpace().pos)
for {
t.peekNonSpace() // skip leading spaces.
operand := t.operand()
if operand != nil {
cmd.append(operand)
}
switch token := t.next(); token.typ {
case itemSpace:
continue
case itemError:
t.errorf("%s", token.val)
case itemRightDelim, itemRightParen:
t.backup()
case itemPipe:
default:
t.errorf("unexpected %s in operand; missing space?", token)
}
break
}
if len(cmd.Args) == 0 {
t.errorf("empty command")
}
return cmd
}
// operand:
// term .Field*
// An operand is a space-separated component of a command,
// a term possibly followed by field accesses.
// A nil return means the next item is not an operand.
func (t *Tree) operand() Node {
node := t.term()
if node == nil {
return nil
}
if t.peek().typ == itemField {
chain := t.newChain(t.peek().pos, node)
for t.peek().typ == itemField {
chain.Add(t.next().val)
}
// Compatibility with original API: If the term is of type NodeField
// or NodeVariable, just put more fields on the original.
// Otherwise, keep the Chain node.
// TODO: Switch to Chains always when we can.
switch node.Type() {
case NodeField:
node = t.newField(chain.Position(), chain.String())
case NodeVariable:
node = t.newVariable(chain.Position(), chain.String())
default:
node = chain
}
}
return node
}
// term:
// literal (number, string, nil, boolean)
// function (identifier)
// .
// .Field
// $
// '(' pipeline ')'
// A term is a simple "expression".
// A nil return means the next item is not a term.
func (t *Tree) term() Node {
switch token := t.nextNonSpace(); token.typ {
case itemError:
t.errorf("%s", token.val)
case itemIdentifier:
if !t.hasFunction(token.val) {
t.errorf("function %q not defined", token.val)
}
return NewIdentifier(token.val).SetTree(t).SetPos(token.pos)
case itemDot:
return t.newDot(token.pos)
case itemNil:
return t.newNil(token.pos)
case itemVariable:
return t.useVar(token.pos, token.val)
case itemField:
return t.newField(token.pos, token.val)
case itemBool:
return t.newBool(token.pos, token.val == "true")
case itemCharConstant, itemComplex, itemNumber:
number, err := t.newNumber(token.pos, token.val, token.typ)
if err != nil {
t.error(err)
}
return number
case itemLeftParen:
pipe := t.pipeline("parenthesized pipeline")
if token := t.next(); token.typ != itemRightParen {
t.errorf("unclosed right paren: unexpected %s", token)
}
return pipe
case itemString, itemRawString:
s, err := strconv.Unquote(token.val)
if err != nil {
t.error(err)
}
return t.newString(token.pos, token.val, s)
}
t.backup()
return nil
}
// hasFunction reports if a function name exists in the Tree's maps.
func (t *Tree) hasFunction(name string) bool {
for _, funcMap := range t.funcs {
if funcMap == nil {
continue
}
if funcMap[name] != nil {
return true
}
}
return false
}
// popVars trims the variable list to the specified length
func (t *Tree) popVars(n int) {
t.vars = t.vars[:n]
}
// useVar returns a node for a variable reference. It errors if the
// variable is not defined.
func (t *Tree) useVar(pos Pos, name string) Node {
v := t.newVariable(pos, name)
for _, varName := range t.vars {
if varName == v.Ident[0] {
return v
}
}
t.errorf("undefined variable %q", v.Ident[0])
return nil
}

218
vendor/github.com/alecthomas/template/template.go generated vendored Normal file
View File

@@ -0,0 +1,218 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package template
import (
"fmt"
"reflect"
"github.com/alecthomas/template/parse"
)
// common holds the information shared by related templates.
type common struct {
tmpl map[string]*Template
// We use two maps, one for parsing and one for execution.
// This separation makes the API cleaner since it doesn't
// expose reflection to the client.
parseFuncs FuncMap
execFuncs map[string]reflect.Value
}
// Template is the representation of a parsed template. The *parse.Tree
// field is exported only for use by html/template and should be treated
// as unexported by all other clients.
type Template struct {
name string
*parse.Tree
*common
leftDelim string
rightDelim string
}
// New allocates a new template with the given name.
func New(name string) *Template {
return &Template{
name: name,
}
}
// Name returns the name of the template.
func (t *Template) Name() string {
return t.name
}
// New allocates a new template associated with the given one and with the same
// delimiters. The association, which is transitive, allows one template to
// invoke another with a {{template}} action.
func (t *Template) New(name string) *Template {
t.init()
return &Template{
name: name,
common: t.common,
leftDelim: t.leftDelim,
rightDelim: t.rightDelim,
}
}
func (t *Template) init() {
if t.common == nil {
t.common = new(common)
t.tmpl = make(map[string]*Template)
t.parseFuncs = make(FuncMap)
t.execFuncs = make(map[string]reflect.Value)
}
}
// Clone returns a duplicate of the template, including all associated
// templates. The actual representation is not copied, but the name space of
// associated templates is, so further calls to Parse in the copy will add
// templates to the copy but not to the original. Clone can be used to prepare
// common templates and use them with variant definitions for other templates
// by adding the variants after the clone is made.
func (t *Template) Clone() (*Template, error) {
nt := t.copy(nil)
nt.init()
nt.tmpl[t.name] = nt
for k, v := range t.tmpl {
if k == t.name { // Already installed.
continue
}
// The associated templates share nt's common structure.
tmpl := v.copy(nt.common)
nt.tmpl[k] = tmpl
}
for k, v := range t.parseFuncs {
nt.parseFuncs[k] = v
}
for k, v := range t.execFuncs {
nt.execFuncs[k] = v
}
return nt, nil
}
// copy returns a shallow copy of t, with common set to the argument.
func (t *Template) copy(c *common) *Template {
nt := New(t.name)
nt.Tree = t.Tree
nt.common = c
nt.leftDelim = t.leftDelim
nt.rightDelim = t.rightDelim
return nt
}
// AddParseTree creates a new template with the name and parse tree
// and associates it with t.
func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
if t.common != nil && t.tmpl[name] != nil {
return nil, fmt.Errorf("template: redefinition of template %q", name)
}
nt := t.New(name)
nt.Tree = tree
t.tmpl[name] = nt
return nt, nil
}
// Templates returns a slice of the templates associated with t, including t
// itself.
func (t *Template) Templates() []*Template {
if t.common == nil {
return nil
}
// Return a slice so we don't expose the map.
m := make([]*Template, 0, len(t.tmpl))
for _, v := range t.tmpl {
m = append(m, v)
}
return m
}
// Delims sets the action delimiters to the specified strings, to be used in
// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
// definitions will inherit the settings. An empty delimiter stands for the
// corresponding default: {{ or }}.
// The return value is the template, so calls can be chained.
func (t *Template) Delims(left, right string) *Template {
t.leftDelim = left
t.rightDelim = right
return t
}
// Funcs adds the elements of the argument map to the template's function map.
// It panics if a value in the map is not a function with appropriate return
// type. However, it is legal to overwrite elements of the map. The return
// value is the template, so calls can be chained.
func (t *Template) Funcs(funcMap FuncMap) *Template {
t.init()
addValueFuncs(t.execFuncs, funcMap)
addFuncs(t.parseFuncs, funcMap)
return t
}
// Lookup returns the template with the given name that is associated with t,
// or nil if there is no such template.
func (t *Template) Lookup(name string) *Template {
if t.common == nil {
return nil
}
return t.tmpl[name]
}
// Parse parses a string into a template. Nested template definitions will be
// associated with the top-level template t. Parse may be called multiple times
// to parse definitions of templates to associate with t. It is an error if a
// resulting template is non-empty (contains content other than template
// definitions) and would replace a non-empty template with the same name.
// (In multiple calls to Parse with the same receiver template, only one call
// can contain text other than space, comments, and template definitions.)
func (t *Template) Parse(text string) (*Template, error) {
t.init()
trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
if err != nil {
return nil, err
}
// Add the newly parsed trees, including the one for t, into our common structure.
for name, tree := range trees {
// If the name we parsed is the name of this template, overwrite this template.
// The associate method checks it's not a redefinition.
tmpl := t
if name != t.name {
tmpl = t.New(name)
}
// Even if t == tmpl, we need to install it in the common.tmpl map.
if replace, err := t.associate(tmpl, tree); err != nil {
return nil, err
} else if replace {
tmpl.Tree = tree
}
tmpl.leftDelim = t.leftDelim
tmpl.rightDelim = t.rightDelim
}
return t, nil
}
// associate installs the new template into the group of templates associated
// with t. It is an error to reuse a name except to overwrite an empty
// template. The two are already known to share the common structure.
// The boolean return value reports wither to store this tree as t.Tree.
func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) {
if new.common != t.common {
panic("internal error: associate not common")
}
name := new.name
if old := t.tmpl[name]; old != nil {
oldIsEmpty := parse.IsEmptyTree(old.Root)
newIsEmpty := parse.IsEmptyTree(tree.Root)
if newIsEmpty {
// Whether old is empty or not, new is empty; no reason to replace old.
return false, nil
}
if !oldIsEmpty {
return false, fmt.Errorf("template: redefinition of template %q", name)
}
}
t.tmpl[name] = new
return true, nil
}

19
vendor/github.com/alecthomas/units/COPYING generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (C) 2014 Alec Thomas
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

83
vendor/github.com/alecthomas/units/bytes.go generated vendored Normal file
View File

@@ -0,0 +1,83 @@
package units
// Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte,
// etc.).
type Base2Bytes int64
// Base-2 byte units.
const (
Kibibyte Base2Bytes = 1024
KiB = Kibibyte
Mebibyte = Kibibyte * 1024
MiB = Mebibyte
Gibibyte = Mebibyte * 1024
GiB = Gibibyte
Tebibyte = Gibibyte * 1024
TiB = Tebibyte
Pebibyte = Tebibyte * 1024
PiB = Pebibyte
Exbibyte = Pebibyte * 1024
EiB = Exbibyte
)
var (
bytesUnitMap = MakeUnitMap("iB", "B", 1024)
oldBytesUnitMap = MakeUnitMap("B", "B", 1024)
)
// ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB
// and KiB are both 1024.
func ParseBase2Bytes(s string) (Base2Bytes, error) {
n, err := ParseUnit(s, bytesUnitMap)
if err != nil {
n, err = ParseUnit(s, oldBytesUnitMap)
}
return Base2Bytes(n), err
}
func (b Base2Bytes) String() string {
return ToString(int64(b), 1024, "iB", "B")
}
var (
metricBytesUnitMap = MakeUnitMap("B", "B", 1000)
)
// MetricBytes are SI byte units (1000 bytes in a kilobyte).
type MetricBytes SI
// SI base-10 byte units.
const (
Kilobyte MetricBytes = 1000
KB = Kilobyte
Megabyte = Kilobyte * 1000
MB = Megabyte
Gigabyte = Megabyte * 1000
GB = Gigabyte
Terabyte = Gigabyte * 1000
TB = Terabyte
Petabyte = Terabyte * 1000
PB = Petabyte
Exabyte = Petabyte * 1000
EB = Exabyte
)
// ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes.
func ParseMetricBytes(s string) (MetricBytes, error) {
n, err := ParseUnit(s, metricBytesUnitMap)
return MetricBytes(n), err
}
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.
func ParseStrictBytes(s string) (int64, error) {
n, err := ParseUnit(s, bytesUnitMap)
if err != nil {
n, err = ParseUnit(s, metricBytesUnitMap)
}
return int64(n), err
}

13
vendor/github.com/alecthomas/units/doc.go generated vendored Normal file
View File

@@ -0,0 +1,13 @@
// Package units provides helpful unit multipliers and functions for Go.
//
// The goal of this package is to have functionality similar to the time [1] package.
//
//
// [1] http://golang.org/pkg/time/
//
// It allows for code like this:
//
// n, err := ParseBase2Bytes("1KB")
// // n == 1024
// n = units.Mebibyte * 512
package units

26
vendor/github.com/alecthomas/units/si.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
package units
// SI units.
type SI int64
// SI unit multiples.
const (
Kilo SI = 1000
Mega = Kilo * 1000
Giga = Mega * 1000
Tera = Giga * 1000
Peta = Tera * 1000
Exa = Peta * 1000
)
func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 {
return map[string]float64{
shortSuffix: 1,
"K" + suffix: float64(scale),
"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),
}
}

138
vendor/github.com/alecthomas/units/util.go generated vendored Normal file
View File

@@ -0,0 +1,138 @@
package units
import (
"errors"
"fmt"
"strings"
)
var (
siUnits = []string{"", "K", "M", "G", "T", "P", "E"}
)
func ToString(n int64, scale int64, suffix, baseSuffix string) string {
mn := len(siUnits)
out := make([]string, mn)
for i, m := range siUnits {
if n%scale != 0 || i == 0 && n == 0 {
s := suffix
if i == 0 {
s = baseSuffix
}
out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s)
}
n /= scale
if n == 0 {
break
}
}
return strings.Join(out, "")
}
// Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123
var errLeadingInt = errors.New("units: bad [0-9]*") // never printed
// leadingInt consumes the leading [0-9]* from s.
func leadingInt(s string) (x int64, rem string, err error) {
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if x >= (1<<63-10)/10 {
// overflow
return 0, "", errLeadingInt
}
x = x*10 + int64(c) - '0'
}
return x, s[i:], nil
}
func ParseUnit(s string, unitMap map[string]float64) (int64, error) {
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
orig := s
f := float64(0)
neg := false
// Consume [-+]?
if s != "" {
c := s[0]
if c == '-' || c == '+' {
neg = c == '-'
s = s[1:]
}
}
// Special case: if all that is left is "0", this is zero.
if s == "0" {
return 0, nil
}
if s == "" {
return 0, errors.New("units: invalid " + orig)
}
for s != "" {
g := float64(0) // this element of the sequence
var x int64
var err error
// The next character must be [0-9.]
if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) {
return 0, errors.New("units: invalid " + orig)
}
// Consume [0-9]*
pl := len(s)
x, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("units: invalid " + orig)
}
g = float64(x)
pre := pl != len(s) // whether we consumed anything before a period
// Consume (\.[0-9]*)?
post := false
if s != "" && s[0] == '.' {
s = s[1:]
pl := len(s)
x, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("units: invalid " + orig)
}
scale := 1.0
for n := pl - len(s); n > 0; n-- {
scale *= 10
}
g += float64(x) / scale
post = pl != len(s)
}
if !pre && !post {
// no digits (e.g. ".s" or "-.s")
return 0, errors.New("units: invalid " + orig)
}
// Consume unit.
i := 0
for ; i < len(s); i++ {
c := s[i]
if c == '.' || ('0' <= c && c <= '9') {
break
}
}
u := s[:i]
s = s[i:]
unit, ok := unitMap[u]
if !ok {
return 0, errors.New("units: unknown unit " + u + " in " + orig)
}
f += g * unit
}
if neg {
f = -f
}
if f < float64(-1<<63) || f > float64(1<<63-1) {
return 0, errors.New("units: overflow parsing unit")
}
return int64(f), nil
}

File diff suppressed because it is too large Load Diff

201
vendor/github.com/dimchansky/utfbom/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,201 @@
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.

192
vendor/github.com/dimchansky/utfbom/utfbom.go generated vendored Normal file
View File

@@ -0,0 +1,192 @@
// Package utfbom implements the detection of the BOM (Unicode Byte Order Mark) and removing as necessary.
// It wraps an io.Reader object, creating another object (Reader) that also implements the io.Reader
// interface but provides automatic BOM checking and removing as necessary.
package utfbom
import (
"errors"
"io"
)
// Encoding is type alias for detected UTF encoding.
type Encoding int
// Constants to identify detected UTF encodings.
const (
// Unknown encoding, returned when no BOM was detected
Unknown Encoding = iota
// UTF8, BOM bytes: EF BB BF
UTF8
// UTF-16, big-endian, BOM bytes: FE FF
UTF16BigEndian
// UTF-16, little-endian, BOM bytes: FF FE
UTF16LittleEndian
// UTF-32, big-endian, BOM bytes: 00 00 FE FF
UTF32BigEndian
// UTF-32, little-endian, BOM bytes: FF FE 00 00
UTF32LittleEndian
)
// String returns a user-friendly string representation of the encoding. Satisfies fmt.Stringer interface.
func (e Encoding) String() string {
switch e {
case UTF8:
return "UTF8"
case UTF16BigEndian:
return "UTF16BigEndian"
case UTF16LittleEndian:
return "UTF16LittleEndian"
case UTF32BigEndian:
return "UTF32BigEndian"
case UTF32LittleEndian:
return "UTF32LittleEndian"
default:
return "Unknown"
}
}
const maxConsecutiveEmptyReads = 100
// Skip creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary.
// It also returns the encoding detected by the BOM.
// If the detected encoding is not needed, you can call the SkipOnly function.
func Skip(rd io.Reader) (*Reader, Encoding) {
// Is it already a Reader?
b, ok := rd.(*Reader)
if ok {
return b, Unknown
}
enc, left, err := detectUtf(rd)
return &Reader{
rd: rd,
buf: left,
err: err,
}, enc
}
// SkipOnly creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary.
func SkipOnly(rd io.Reader) *Reader {
r, _ := Skip(rd)
return r
}
// Reader implements automatic BOM (Unicode Byte Order Mark) checking and
// removing as necessary for an io.Reader object.
type Reader struct {
rd io.Reader // reader provided by the client
buf []byte // buffered data
err error // last error
}
// Read is an implementation of io.Reader interface.
// The bytes are taken from the underlying Reader, but it checks for BOMs, removing them as necessary.
func (r *Reader) Read(p []byte) (n int, err error) {
if len(p) == 0 {
return 0, nil
}
if r.buf == nil {
if r.err != nil {
return 0, r.readErr()
}
return r.rd.Read(p)
}
// copy as much as we can
n = copy(p, r.buf)
r.buf = nilIfEmpty(r.buf[n:])
return n, nil
}
func (r *Reader) readErr() error {
err := r.err
r.err = nil
return err
}
var errNegativeRead = errors.New("utfbom: reader returned negative count from Read")
func detectUtf(rd io.Reader) (enc Encoding, buf []byte, err error) {
buf, err = readBOM(rd)
if len(buf) >= 4 {
if isUTF32BigEndianBOM4(buf) {
return UTF32BigEndian, nilIfEmpty(buf[4:]), err
}
if isUTF32LittleEndianBOM4(buf) {
return UTF32LittleEndian, nilIfEmpty(buf[4:]), err
}
}
if len(buf) > 2 && isUTF8BOM3(buf) {
return UTF8, nilIfEmpty(buf[3:]), err
}
if (err != nil && err != io.EOF) || (len(buf) < 2) {
return Unknown, nilIfEmpty(buf), err
}
if isUTF16BigEndianBOM2(buf) {
return UTF16BigEndian, nilIfEmpty(buf[2:]), err
}
if isUTF16LittleEndianBOM2(buf) {
return UTF16LittleEndian, nilIfEmpty(buf[2:]), err
}
return Unknown, nilIfEmpty(buf), err
}
func readBOM(rd io.Reader) (buf []byte, err error) {
const maxBOMSize = 4
var bom [maxBOMSize]byte // used to read BOM
// read as many bytes as possible
for nEmpty, n := 0, 0; err == nil && len(buf) < maxBOMSize; buf = bom[:len(buf)+n] {
if n, err = rd.Read(bom[len(buf):]); n < 0 {
panic(errNegativeRead)
}
if n > 0 {
nEmpty = 0
} else {
nEmpty++
if nEmpty >= maxConsecutiveEmptyReads {
err = io.ErrNoProgress
}
}
}
return
}
func isUTF32BigEndianBOM4(buf []byte) bool {
return buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0xFE && buf[3] == 0xFF
}
func isUTF32LittleEndianBOM4(buf []byte) bool {
return buf[0] == 0xFF && buf[1] == 0xFE && buf[2] == 0x00 && buf[3] == 0x00
}
func isUTF8BOM3(buf []byte) bool {
return buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF
}
func isUTF16BigEndianBOM2(buf []byte) bool {
return buf[0] == 0xFE && buf[1] == 0xFF
}
func isUTF16LittleEndianBOM2(buf []byte) bool {
return buf[0] == 0xFF && buf[1] == 0xFE
}
func nilIfEmpty(buf []byte) (res []byte) {
if len(buf) > 0 {
res = buf
}
return
}

Some files were not shown because too many files have changed in this diff Show More