Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
doodoocoder
prometheus
提交
1fb2190e
P
prometheus
项目概览
doodoocoder
/
prometheus
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
prometheus
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
1fb2190e
编写于
4月 11, 2017
作者:
B
Björn Rabenstein
提交者:
GitHub
4月 11, 2017
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #2607 from prometheus/beorn7/storage
Vendoring update prior to 1.6 release
上级
0e0fc5a7
c53f256a
变更
22
隐藏空白更改
内联
并排
Showing
22 changed file
with
305 addition
and
1684 deletion
+305
-1684
storage/local/mapper.go
storage/local/mapper.go
+1
-1
vendor/cloud.google.com/go/LICENSE
vendor/cloud.google.com/go/LICENSE
+0
-202
vendor/cloud.google.com/go/compute/metadata/metadata.go
vendor/cloud.google.com/go/compute/metadata/metadata.go
+0
-438
vendor/cloud.google.com/go/internal/cloud.go
vendor/cloud.google.com/go/internal/cloud.go
+0
-64
vendor/github.com/prometheus/client_golang/prometheus/counter.go
...github.com/prometheus/client_golang/prometheus/counter.go
+2
-10
vendor/github.com/prometheus/client_golang/prometheus/desc.go
...or/github.com/prometheus/client_golang/prometheus/desc.go
+4
-9
vendor/github.com/prometheus/client_golang/prometheus/doc.go
vendor/github.com/prometheus/client_golang/prometheus/doc.go
+34
-30
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
...r/github.com/prometheus/client_golang/prometheus/gauge.go
+9
-4
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
...b.com/prometheus/client_golang/prometheus/go_collector.go
+28
-15
vendor/github.com/prometheus/client_golang/prometheus/http.go
...or/github.com/prometheus/client_golang/prometheus/http.go
+66
-30
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
.../prometheus/client_golang/prometheus/process_collector.go
+51
-53
vendor/github.com/prometheus/client_golang/prometheus/registry.go
...ithub.com/prometheus/client_golang/prometheus/registry.go
+3
-54
vendor/github.com/prometheus/client_golang/prometheus/summary.go
...github.com/prometheus/client_golang/prometheus/summary.go
+13
-4
vendor/github.com/prometheus/client_golang/prometheus/timer.go
...r/github.com/prometheus/client_golang/prometheus/timer.go
+74
-0
vendor/github.com/prometheus/client_golang/prometheus/untyped.go
...github.com/prometheus/client_golang/prometheus/untyped.go
+5
-0
vendor/github.com/prometheus/client_golang/prometheus/value.go
...r/github.com/prometheus/client_golang/prometheus/value.go
+6
-1
vendor/github.com/vaughan0/go-ini/LICENSE
vendor/github.com/vaughan0/go-ini/LICENSE
+0
-14
vendor/github.com/vaughan0/go-ini/README.md
vendor/github.com/vaughan0/go-ini/README.md
+0
-70
vendor/github.com/vaughan0/go-ini/ini.go
vendor/github.com/vaughan0/go-ini/ini.go
+0
-123
vendor/github.com/vaughan0/go-ini/test.ini
vendor/github.com/vaughan0/go-ini/test.ini
+0
-2
vendor/golang.org/x/net/internal/timeseries/timeseries.go
vendor/golang.org/x/net/internal/timeseries/timeseries.go
+0
-525
vendor/vendor.json
vendor/vendor.json
+9
-35
未找到文件。
storage/local/mapper.go
浏览文件 @
1fb2190e
...
...
@@ -67,7 +67,7 @@ func newFPMapper(fpToSeries *seriesMap, p *persistence) (*fpMapper, error) {
return
nil
,
err
}
m
.
mappings
=
mappings
m
.
mappingsCounter
.
Set
(
float64
(
len
(
m
.
mappings
)))
m
.
mappingsCounter
.
Add
(
float64
(
len
(
m
.
mappings
)))
m
.
highestMappedFP
=
nextFP
return
m
,
nil
}
...
...
vendor/cloud.google.com/go/LICENSE
已删除
100644 → 0
浏览文件 @
0e0fc5a7
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 2014 Google Inc.
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.
vendor/cloud.google.com/go/compute/metadata/metadata.go
已删除
100644 → 0
浏览文件 @
0e0fc5a7
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package metadata provides access to Google Compute Engine (GCE)
// metadata and API service accounts.
//
// This package is a wrapper around the GCE metadata service,
// as documented at https://developers.google.com/compute/docs/metadata.
package
metadata
import
(
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"runtime"
"strings"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
"cloud.google.com/go/internal"
)
const
(
// metadataIP is the documented metadata server IP address.
metadataIP
=
"169.254.169.254"
// metadataHostEnv is the environment variable specifying the
// GCE metadata hostname. If empty, the default value of
// metadataIP ("169.254.169.254") is used instead.
// This is variable name is not defined by any spec, as far as
// I know; it was made up for the Go package.
metadataHostEnv
=
"GCE_METADATA_HOST"
)
type
cachedValue
struct
{
k
string
trim
bool
mu
sync
.
Mutex
v
string
}
var
(
projID
=
&
cachedValue
{
k
:
"project/project-id"
,
trim
:
true
}
projNum
=
&
cachedValue
{
k
:
"project/numeric-project-id"
,
trim
:
true
}
instID
=
&
cachedValue
{
k
:
"instance/id"
,
trim
:
true
}
)
var
(
metaClient
=
&
http
.
Client
{
Transport
:
&
internal
.
Transport
{
Base
:
&
http
.
Transport
{
Dial
:
(
&
net
.
Dialer
{
Timeout
:
2
*
time
.
Second
,
KeepAlive
:
30
*
time
.
Second
,
})
.
Dial
,
ResponseHeaderTimeout
:
2
*
time
.
Second
,
},
},
}
subscribeClient
=
&
http
.
Client
{
Transport
:
&
internal
.
Transport
{
Base
:
&
http
.
Transport
{
Dial
:
(
&
net
.
Dialer
{
Timeout
:
2
*
time
.
Second
,
KeepAlive
:
30
*
time
.
Second
,
})
.
Dial
,
},
},
}
)
// NotDefinedError is returned when requested metadata is not defined.
//
// The underlying string is the suffix after "/computeMetadata/v1/".
//
// This error is not returned if the value is defined to be the empty
// string.
type
NotDefinedError
string
func
(
suffix
NotDefinedError
)
Error
()
string
{
return
fmt
.
Sprintf
(
"metadata: GCE metadata %q not defined"
,
string
(
suffix
))
}
// Get returns a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
//
// If the GCE_METADATA_HOST environment variable is not defined, a default of
// 169.254.169.254 will be used instead.
//
// If the requested metadata is not defined, the returned error will
// be of type NotDefinedError.
func
Get
(
suffix
string
)
(
string
,
error
)
{
val
,
_
,
err
:=
getETag
(
metaClient
,
suffix
)
return
val
,
err
}
// getETag returns a value from the metadata service as well as the associated
// ETag using the provided client. This func is otherwise equivalent to Get.
func
getETag
(
client
*
http
.
Client
,
suffix
string
)
(
value
,
etag
string
,
err
error
)
{
// Using a fixed IP makes it very difficult to spoof the metadata service in
// a container, which is an important use-case for local testing of cloud
// deployments. To enable spoofing of the metadata service, the environment
// variable GCE_METADATA_HOST is first inspected to decide where metadata
// requests shall go.
host
:=
os
.
Getenv
(
metadataHostEnv
)
if
host
==
""
{
// Using 169.254.169.254 instead of "metadata" here because Go
// binaries built with the "netgo" tag and without cgo won't
// know the search suffix for "metadata" is
// ".google.internal", and this IP address is documented as
// being stable anyway.
host
=
metadataIP
}
url
:=
"http://"
+
host
+
"/computeMetadata/v1/"
+
suffix
req
,
_
:=
http
.
NewRequest
(
"GET"
,
url
,
nil
)
req
.
Header
.
Set
(
"Metadata-Flavor"
,
"Google"
)
res
,
err
:=
client
.
Do
(
req
)
if
err
!=
nil
{
return
""
,
""
,
err
}
defer
res
.
Body
.
Close
()
if
res
.
StatusCode
==
http
.
StatusNotFound
{
return
""
,
""
,
NotDefinedError
(
suffix
)
}
if
res
.
StatusCode
!=
200
{
return
""
,
""
,
fmt
.
Errorf
(
"status code %d trying to fetch %s"
,
res
.
StatusCode
,
url
)
}
all
,
err
:=
ioutil
.
ReadAll
(
res
.
Body
)
if
err
!=
nil
{
return
""
,
""
,
err
}
return
string
(
all
),
res
.
Header
.
Get
(
"Etag"
),
nil
}
func
getTrimmed
(
suffix
string
)
(
s
string
,
err
error
)
{
s
,
err
=
Get
(
suffix
)
s
=
strings
.
TrimSpace
(
s
)
return
}
func
(
c
*
cachedValue
)
get
()
(
v
string
,
err
error
)
{
defer
c
.
mu
.
Unlock
()
c
.
mu
.
Lock
()
if
c
.
v
!=
""
{
return
c
.
v
,
nil
}
if
c
.
trim
{
v
,
err
=
getTrimmed
(
c
.
k
)
}
else
{
v
,
err
=
Get
(
c
.
k
)
}
if
err
==
nil
{
c
.
v
=
v
}
return
}
var
(
onGCEOnce
sync
.
Once
onGCE
bool
)
// OnGCE reports whether this process is running on Google Compute Engine.
func
OnGCE
()
bool
{
onGCEOnce
.
Do
(
initOnGCE
)
return
onGCE
}
func
initOnGCE
()
{
onGCE
=
testOnGCE
()
}
func
testOnGCE
()
bool
{
// The user explicitly said they're on GCE, so trust them.
if
os
.
Getenv
(
metadataHostEnv
)
!=
""
{
return
true
}
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
defer
cancel
()
resc
:=
make
(
chan
bool
,
2
)
// Try two strategies in parallel.
// See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194
go
func
()
{
res
,
err
:=
ctxhttp
.
Get
(
ctx
,
metaClient
,
"http://"
+
metadataIP
)
if
err
!=
nil
{
resc
<-
false
return
}
defer
res
.
Body
.
Close
()
resc
<-
res
.
Header
.
Get
(
"Metadata-Flavor"
)
==
"Google"
}()
go
func
()
{
addrs
,
err
:=
net
.
LookupHost
(
"metadata.google.internal"
)
if
err
!=
nil
||
len
(
addrs
)
==
0
{
resc
<-
false
return
}
resc
<-
strsContains
(
addrs
,
metadataIP
)
}()
tryHarder
:=
systemInfoSuggestsGCE
()
if
tryHarder
{
res
:=
<-
resc
if
res
{
// The first strategy succeeded, so let's use it.
return
true
}
// Wait for either the DNS or metadata server probe to
// contradict the other one and say we are running on
// GCE. Give it a lot of time to do so, since the system
// info already suggests we're running on a GCE BIOS.
timer
:=
time
.
NewTimer
(
5
*
time
.
Second
)
defer
timer
.
Stop
()
select
{
case
res
=
<-
resc
:
return
res
case
<-
timer
.
C
:
// Too slow. Who knows what this system is.
return
false
}
}
// There's no hint from the system info that we're running on
// GCE, so use the first probe's result as truth, whether it's
// true or false. The goal here is to optimize for speed for
// users who are NOT running on GCE. We can't assume that
// either a DNS lookup or an HTTP request to a blackholed IP
// address is fast. Worst case this should return when the
// metaClient's Transport.ResponseHeaderTimeout or
// Transport.Dial.Timeout fires (in two seconds).
return
<-
resc
}
// systemInfoSuggestsGCE reports whether the local system (without
// doing network requests) suggests that we're running on GCE. If this
// returns true, testOnGCE tries a bit harder to reach its metadata
// server.
func
systemInfoSuggestsGCE
()
bool
{
if
runtime
.
GOOS
!=
"linux"
{
// We don't have any non-Linux clues available, at least yet.
return
false
}
slurp
,
_
:=
ioutil
.
ReadFile
(
"/sys/class/dmi/id/product_name"
)
name
:=
strings
.
TrimSpace
(
string
(
slurp
))
return
name
==
"Google"
||
name
==
"Google Compute Engine"
}
// Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters.
//
// Subscribe calls fn with the latest metadata value indicated by the provided
// suffix. If the metadata value is deleted, fn is called with the empty string
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
// is deleted. Subscribe returns the error value returned from the last call to
// fn, which may be nil when ok == false.
func
Subscribe
(
suffix
string
,
fn
func
(
v
string
,
ok
bool
)
error
)
error
{
const
failedSubscribeSleep
=
time
.
Second
*
5
// First check to see if the metadata value exists at all.
val
,
lastETag
,
err
:=
getETag
(
subscribeClient
,
suffix
)
if
err
!=
nil
{
return
err
}
if
err
:=
fn
(
val
,
true
);
err
!=
nil
{
return
err
}
ok
:=
true
if
strings
.
ContainsRune
(
suffix
,
'?'
)
{
suffix
+=
"&wait_for_change=true&last_etag="
}
else
{
suffix
+=
"?wait_for_change=true&last_etag="
}
for
{
val
,
etag
,
err
:=
getETag
(
subscribeClient
,
suffix
+
url
.
QueryEscape
(
lastETag
))
if
err
!=
nil
{
if
_
,
deleted
:=
err
.
(
NotDefinedError
);
!
deleted
{
time
.
Sleep
(
failedSubscribeSleep
)
continue
// Retry on other errors.
}
ok
=
false
}
lastETag
=
etag
if
err
:=
fn
(
val
,
ok
);
err
!=
nil
||
!
ok
{
return
err
}
}
}
// ProjectID returns the current instance's project ID string.
func
ProjectID
()
(
string
,
error
)
{
return
projID
.
get
()
}
// NumericProjectID returns the current instance's numeric project ID.
func
NumericProjectID
()
(
string
,
error
)
{
return
projNum
.
get
()
}
// InternalIP returns the instance's primary internal IP address.
func
InternalIP
()
(
string
,
error
)
{
return
getTrimmed
(
"instance/network-interfaces/0/ip"
)
}
// ExternalIP returns the instance's primary external (public) IP address.
func
ExternalIP
()
(
string
,
error
)
{
return
getTrimmed
(
"instance/network-interfaces/0/access-configs/0/external-ip"
)
}
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func
Hostname
()
(
string
,
error
)
{
return
getTrimmed
(
"instance/hostname"
)
}
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func
InstanceTags
()
([]
string
,
error
)
{
var
s
[]
string
j
,
err
:=
Get
(
"instance/tags"
)
if
err
!=
nil
{
return
nil
,
err
}
if
err
:=
json
.
NewDecoder
(
strings
.
NewReader
(
j
))
.
Decode
(
&
s
);
err
!=
nil
{
return
nil
,
err
}
return
s
,
nil
}
// InstanceID returns the current VM's numeric instance ID.
func
InstanceID
()
(
string
,
error
)
{
return
instID
.
get
()
}
// InstanceName returns the current VM's instance ID string.
func
InstanceName
()
(
string
,
error
)
{
host
,
err
:=
Hostname
()
if
err
!=
nil
{
return
""
,
err
}
return
strings
.
Split
(
host
,
"."
)[
0
],
nil
}
// Zone returns the current VM's zone, such as "us-central1-b".
func
Zone
()
(
string
,
error
)
{
zone
,
err
:=
getTrimmed
(
"instance/zone"
)
// zone is of the form "projects/<projNum>/zones/<zoneName>".
if
err
!=
nil
{
return
""
,
err
}
return
zone
[
strings
.
LastIndex
(
zone
,
"/"
)
+
1
:
],
nil
}
// InstanceAttributes returns the list of user-defined attributes,
// assigned when initially creating a GCE VM instance. The value of an
// attribute can be obtained with InstanceAttributeValue.
func
InstanceAttributes
()
([]
string
,
error
)
{
return
lines
(
"instance/attributes/"
)
}
// ProjectAttributes returns the list of user-defined attributes
// applying to the project as a whole, not just this VM. The value of
// an attribute can be obtained with ProjectAttributeValue.
func
ProjectAttributes
()
([]
string
,
error
)
{
return
lines
(
"project/attributes/"
)
}
func
lines
(
suffix
string
)
([]
string
,
error
)
{
j
,
err
:=
Get
(
suffix
)
if
err
!=
nil
{
return
nil
,
err
}
s
:=
strings
.
Split
(
strings
.
TrimSpace
(
j
),
"
\n
"
)
for
i
:=
range
s
{
s
[
i
]
=
strings
.
TrimSpace
(
s
[
i
])
}
return
s
,
nil
}
// InstanceAttributeValue returns the value of the provided VM
// instance attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// InstanceAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func
InstanceAttributeValue
(
attr
string
)
(
string
,
error
)
{
return
Get
(
"instance/attributes/"
+
attr
)
}
// ProjectAttributeValue returns the value of the provided
// project attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// ProjectAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func
ProjectAttributeValue
(
attr
string
)
(
string
,
error
)
{
return
Get
(
"project/attributes/"
+
attr
)
}
// Scopes returns the service account scopes for the given account.
// The account may be empty or the string "default" to use the instance's
// main account.
func
Scopes
(
serviceAccount
string
)
([]
string
,
error
)
{
if
serviceAccount
==
""
{
serviceAccount
=
"default"
}
return
lines
(
"instance/service-accounts/"
+
serviceAccount
+
"/scopes"
)
}
func
strsContains
(
ss
[]
string
,
s
string
)
bool
{
for
_
,
v
:=
range
ss
{
if
v
==
s
{
return
true
}
}
return
false
}
vendor/cloud.google.com/go/internal/cloud.go
已删除
100644 → 0
浏览文件 @
0e0fc5a7
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package internal provides support for the cloud packages.
//
// Users should not import this package directly.
package
internal
import
(
"fmt"
"net/http"
)
const
userAgent
=
"gcloud-golang/0.1"
// Transport is an http.RoundTripper that appends Google Cloud client's
// user-agent to the original request's user-agent header.
type
Transport
struct
{
// TODO(bradfitz): delete internal.Transport. It's too wrappy for what it does.
// Do User-Agent some other way.
// Base is the actual http.RoundTripper
// requests will use. It must not be nil.
Base
http
.
RoundTripper
}
// RoundTrip appends a user-agent to the existing user-agent
// header and delegates the request to the base http.RoundTripper.
func
(
t
*
Transport
)
RoundTrip
(
req
*
http
.
Request
)
(
*
http
.
Response
,
error
)
{
req
=
cloneRequest
(
req
)
ua
:=
req
.
Header
.
Get
(
"User-Agent"
)
if
ua
==
""
{
ua
=
userAgent
}
else
{
ua
=
fmt
.
Sprintf
(
"%s %s"
,
ua
,
userAgent
)
}
req
.
Header
.
Set
(
"User-Agent"
,
ua
)
return
t
.
Base
.
RoundTrip
(
req
)
}
// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func
cloneRequest
(
r
*
http
.
Request
)
*
http
.
Request
{
// shallow copy of the struct
r2
:=
new
(
http
.
Request
)
*
r2
=
*
r
// deep copy of the Header
r2
.
Header
=
make
(
http
.
Header
)
for
k
,
s
:=
range
r
.
Header
{
r2
.
Header
[
k
]
=
s
}
return
r2
}
vendor/github.com/prometheus/client_golang/prometheus/counter.go
浏览文件 @
1fb2190e
...
...
@@ -30,16 +30,8 @@ type Counter interface {
Metric
Collector
// Set is used to set the Counter to an arbitrary value. It is only used
// if you have to transfer a value from an external counter into this
// Prometheus metric. Do not use it for regular handling of a
// Prometheus counter (as it can be used to break the contract of
// monotonically increasing values).
//
// Deprecated: Use NewConstMetric to create a counter for an external
// value. A Counter should never be set.
Set
(
float64
)
// Inc increments the counter by 1.
// Inc increments the counter by 1. Use Add to increment it by arbitrary
// non-negative values.
Inc
()
// Add adds the given value to the counter. It panics if the value is <
// 0.
...
...
vendor/github.com/prometheus/client_golang/prometheus/desc.go
浏览文件 @
1fb2190e
...
...
@@ -16,20 +16,15 @@ package prometheus
import
(
"errors"
"fmt"
"regexp"
"sort"
"strings"
"github.com/golang/protobuf/proto"
"github.com/prometheus/common/model"
dto
"github.com/prometheus/client_model/go"
)
var
(
metricNameRE
=
regexp
.
MustCompile
(
`^[a-zA-Z_][a-zA-Z0-9_:]*$`
)
labelNameRE
=
regexp
.
MustCompile
(
"^[a-zA-Z_][a-zA-Z0-9_]*$"
)
)
// reservedLabelPrefix is a prefix which is not legal in user-supplied
// label names.
const
reservedLabelPrefix
=
"__"
...
...
@@ -78,7 +73,7 @@ type Desc struct {
// Help string. Each Desc with the same fqName must have the same
// dimHash.
dimHash
uint64
// err is an error that occured during construction. It is reported on
// err is an error that occur
r
ed during construction. It is reported on
// registration time.
err
error
}
...
...
@@ -103,7 +98,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
d
.
err
=
errors
.
New
(
"empty help string"
)
return
d
}
if
!
m
etricNameRE
.
MatchString
(
fqName
)
{
if
!
m
odel
.
IsValidMetricName
(
model
.
LabelValue
(
fqName
)
)
{
d
.
err
=
fmt
.
Errorf
(
"%q is not a valid metric name"
,
fqName
)
return
d
}
...
...
@@ -200,6 +195,6 @@ func (d *Desc) String() string {
}
func
checkLabelName
(
l
string
)
bool
{
return
labelNameRE
.
MatchString
(
l
)
&&
return
model
.
LabelName
(
l
)
.
IsValid
(
)
&&
!
strings
.
HasPrefix
(
l
,
reservedLabelPrefix
)
}
vendor/github.com/prometheus/client_golang/prometheus/doc.go
浏览文件 @
1fb2190e
...
...
@@ -17,7 +17,7 @@
// Pushgateway (package push).
//
// All exported functions and methods are safe to be used concurrently unless
//specified otherwise.
//
specified otherwise.
//
// A Basic Example
//
...
...
@@ -59,7 +59,7 @@
// // The Handler function provides a default handler to expose metrics
// // via an HTTP server. "/metrics" is the usual endpoint for that.
// http.Handle("/metrics", promhttp.Handler())
//
http.ListenAndServe(":8080", nil
)
//
log.Fatal(http.ListenAndServe(":8080", nil)
)
// }
//
//
...
...
@@ -69,7 +69,7 @@
// Metrics
//
// The number of exported identifiers in this package might appear a bit
// overwhelming. Ho
v
ever, in addition to the basic plumbing shown in the example
// overwhelming. Ho
w
ever, in addition to the basic plumbing shown in the example
// above, you only need to understand the different metric types and their
// vector versions for basic usage.
//
...
...
@@ -95,8 +95,8 @@
// SummaryVec, HistogramVec, and UntypedVec are not.
//
// To create instances of Metrics and their vector versions, you need a suitable
// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts,
//
HistogramOpts, or
UntypedOpts.
// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts,
HistogramOpts, or
// UntypedOpts.
//
// Custom Collectors and constant Metrics
//
...
...
@@ -114,8 +114,8 @@
// Metric instances “on the fly” using NewConstMetric, NewConstHistogram, and
// NewConstSummary (and their respective Must… versions). That will happen in
// the Collect method. The Describe method has to return separate Desc
// instances, representative of the “throw-away” metrics to be created
//
later.
NewDesc comes in handy to create those Desc instances.
// instances, representative of the “throw-away” metrics to be created
later.
// NewDesc comes in handy to create those Desc instances.
//
// The Collector example illustrates the use case. You can also look at the
// source code of the processCollector (mirroring process metrics), the
...
...
@@ -129,32 +129,32 @@
// Advanced Uses of the Registry
//
// While MustRegister is the by far most common way of registering a Collector,
// sometimes you might want to handle the errors the registration might
//
cause. As suggested by the name, MustRegister panics if an error occurs. With
//
the
Register function, the error is returned and can be handled.
// sometimes you might want to handle the errors the registration might
cause.
//
As suggested by the name, MustRegister panics if an error occurs. With the
// Register function, the error is returned and can be handled.
//
// An error is returned if the registered Collector is incompatible or
// inconsistent with already registered metrics. The registry aims for
// consistency of the collected metrics according to the Prometheus data
//
model. Inconsistencies are ideally detected at registration time, not a
t
//
collect time. The former will usually be detected at start-up time of a
//
program, while the latter will only happen at scrape time, possibly not even
//
on the first scrape if the inconsistency only becomes relevant later. That is
//
the main reason why a Collector and a Metric have to describe themselves to
//
the
registry.
// consistency of the collected metrics according to the Prometheus data
model.
//
Inconsistencies are ideally detected at registration time, not at collec
t
//
time. The former will usually be detected at start-up time of a program,
//
while the latter will only happen at scrape time, possibly not even on the
//
first scrape if the inconsistency only becomes relevant later. That is the
//
main reason why a Collector and a Metric have to describe themselves to the
// registry.
//
// So far, everything we did operated on the so-called default registry, as it
// can be found in the global DefaultRegistry variable. With NewRegistry, you
// can create a custom registry, or you can even implement the Registerer or
// Gatherer interfaces yourself. The methods Register and Unregister work in
//
the same way on a custom registry as the global functions Register and
//
Unregister
on the default registry.
// Gatherer interfaces yourself. The methods Register and Unregister work in
the
//
same way on a custom registry as the global functions Register and Unregister
// on the default registry.
//
// There are a number of uses for custom registries: You can use registries
//
with special properties, see NewPedanticRegistry. You can avoid global state,
//
as it is imposed by the DefaultRegistry. You can use multiple registries at
//
the same time to expose different metrics in different ways. You can us
e
//
separate
registries for testing purposes.
// There are a number of uses for custom registries: You can use registries
with
//
special properties, see NewPedanticRegistry. You can avoid global state, as
//
it is imposed by the DefaultRegistry. You can use multiple registries at the
//
same time to expose different metrics in different ways. You can use separat
e
// registries for testing purposes.
//
// Also note that the DefaultRegistry comes registered with a Collector for Go
// runtime metrics (via NewGoCollector) and a Collector for process metrics (via
...
...
@@ -166,16 +166,20 @@
// The Registry implements the Gatherer interface. The caller of the Gather
// method can then expose the gathered metrics in some way. Usually, the metrics
// are served via HTTP on the /metrics endpoint. That's happening in the example
// above. The tools to expose metrics via HTTP are in the promhttp
// sub-package. (The top-level functions in the prometheus package are
// deprecated.)
// above. The tools to expose metrics via HTTP are in the promhttp sub-package.
// (The top-level functions in the prometheus package are deprecated.)
//
// Pushing to the Pushgateway
//
// Function for pushing to the Pushgateway can be found in the push sub-package.
//
// Graphite Bridge
//
// Functions and examples to push metrics from a Gatherer to Graphite can be
// found in the graphite sub-package.
//
// Other Means of Exposition
//
// More ways of exposing metrics can easily be added
. Sending metrics to
//
Graphite would be an example that will soon be implemented
.
// More ways of exposing metrics can easily be added
by following the approaches
//
of the existing implementations
.
package
prometheus
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
浏览文件 @
1fb2190e
...
...
@@ -27,16 +27,21 @@ type Gauge interface {
// Set sets the Gauge to an arbitrary value.
Set
(
float64
)
// Inc increments the Gauge by 1.
// Inc increments the Gauge by 1. Use Add to increment it by arbitrary
// values.
Inc
()
// Dec decrements the Gauge by 1.
// Dec decrements the Gauge by 1. Use Sub to decrement it by arbitrary
// values.
Dec
()
// Add adds the given value to the Gauge. (The value can be
//
negative,
resulting in a decrease of the Gauge.)
// Add adds the given value to the Gauge. (The value can be
negative,
// resulting in a decrease of the Gauge.)
Add
(
float64
)
// Sub subtracts the given value from the Gauge. (The value can be
// negative, resulting in an increase of the Gauge.)
Sub
(
float64
)
// SetToCurrentTime sets the Gauge to the current Unix time in seconds.
SetToCurrentTime
()
}
// GaugeOpts is an alias for Opts. See there for doc comments.
...
...
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
浏览文件 @
1fb2190e
...
...
@@ -8,8 +8,9 @@ import (
)
type
goCollector
struct
{
goroutines
Gauge
gcDesc
*
Desc
goroutinesDesc
*
Desc
threadsDesc
*
Desc
gcDesc
*
Desc
// metrics to describe and collect
metrics
memStatsMetrics
...
...
@@ -19,11 +20,14 @@ type goCollector struct {
// go process.
func
NewGoCollector
()
Collector
{
return
&
goCollector
{
goroutines
:
NewGauge
(
GaugeOpts
{
Namespace
:
"go"
,
Name
:
"goroutines"
,
Help
:
"Number of goroutines that currently exist."
,
}),
goroutinesDesc
:
NewDesc
(
"go_goroutines"
,
"Number of goroutines that currently exist."
,
nil
,
nil
),
threadsDesc
:
NewDesc
(
"go_threads"
,
"Number of OS threads created"
,
nil
,
nil
),
gcDesc
:
NewDesc
(
"go_gc_duration_seconds"
,
"A summary of the GC invocation durations."
,
...
...
@@ -48,7 +52,7 @@ func NewGoCollector() Collector {
},
{
desc
:
NewDesc
(
memstatNamespace
(
"sys_bytes"
),
"Number of bytes obtained
by system. Sum of all system allocations
."
,
"Number of bytes obtained
from system
."
,
nil
,
nil
,
),
eval
:
func
(
ms
*
runtime
.
MemStats
)
float64
{
return
float64
(
ms
.
Sys
)
},
...
...
@@ -111,12 +115,12 @@ func NewGoCollector() Collector {
valType
:
GaugeValue
,
},
{
desc
:
NewDesc
(
memstatNamespace
(
"heap_released_bytes
_total
"
),
"
Total n
umber of heap bytes released to OS."
,
memstatNamespace
(
"heap_released_bytes"
),
"
N
umber of heap bytes released to OS."
,
nil
,
nil
,
),
eval
:
func
(
ms
*
runtime
.
MemStats
)
float64
{
return
float64
(
ms
.
HeapReleased
)
},
valType
:
Counter
Value
,
valType
:
Gauge
Value
,
},
{
desc
:
NewDesc
(
memstatNamespace
(
"heap_objects"
),
...
...
@@ -213,6 +217,14 @@ func NewGoCollector() Collector {
),
eval
:
func
(
ms
*
runtime
.
MemStats
)
float64
{
return
float64
(
ms
.
LastGC
)
/
1e9
},
valType
:
GaugeValue
,
},
{
desc
:
NewDesc
(
memstatNamespace
(
"gc_cpu_fraction"
),
"The fraction of this program's available CPU time used by the GC since the program started."
,
nil
,
nil
,
),
eval
:
func
(
ms
*
runtime
.
MemStats
)
float64
{
return
ms
.
GCCPUFraction
},
valType
:
GaugeValue
,
},
},
}
...
...
@@ -224,9 +236,9 @@ func memstatNamespace(s string) string {
// Describe returns all descriptions of the collector.
func
(
c
*
goCollector
)
Describe
(
ch
chan
<-
*
Desc
)
{
ch
<-
c
.
goroutines
.
Desc
()
ch
<-
c
.
goroutinesDesc
ch
<-
c
.
threadsDesc
ch
<-
c
.
gcDesc
for
_
,
i
:=
range
c
.
metrics
{
ch
<-
i
.
desc
}
...
...
@@ -234,8 +246,9 @@ func (c *goCollector) Describe(ch chan<- *Desc) {
// Collect returns the current state of all metrics of the collector.
func
(
c
*
goCollector
)
Collect
(
ch
chan
<-
Metric
)
{
c
.
goroutines
.
Set
(
float64
(
runtime
.
NumGoroutine
()))
ch
<-
c
.
goroutines
ch
<-
MustNewConstMetric
(
c
.
goroutinesDesc
,
GaugeValue
,
float64
(
runtime
.
NumGoroutine
()))
n
,
_
:=
runtime
.
ThreadCreateProfile
(
nil
)
ch
<-
MustNewConstMetric
(
c
.
threadsDesc
,
GaugeValue
,
float64
(
n
))
var
stats
debug
.
GCStats
stats
.
PauseQuantiles
=
make
([]
time
.
Duration
,
5
)
...
...
vendor/github.com/prometheus/client_golang/prometheus/http.go
浏览文件 @
1fb2190e
...
...
@@ -62,7 +62,7 @@ func giveBuf(buf *bytes.Buffer) {
//
// Deprecated: Please note the issues described in the doc comment of
// InstrumentHandler. You might want to consider using promhttp.Handler instead
// (which is no
n
instrumented).
// (which is no
t
instrumented).
func
Handler
()
http
.
Handler
{
return
InstrumentHandler
(
"prometheus"
,
UninstrumentedHandler
())
}
...
...
@@ -172,6 +172,9 @@ func nowSeries(t ...time.Time) nower {
// httputil.ReverseProxy is a prominent example for a handler
// performing such writes.
//
// - It has additional issues with HTTP/2, cf.
// https://github.com/prometheus/client_golang/issues/272.
//
// Upcoming versions of this package will provide ways of instrumenting HTTP
// handlers that are more flexible and have fewer issues. Please prefer direct
// instrumentation in the meantime.
...
...
@@ -190,6 +193,7 @@ func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWri
SummaryOpts
{
Subsystem
:
"http"
,
ConstLabels
:
Labels
{
"handler"
:
handlerName
},
Objectives
:
map
[
float64
]
float64
{
0.5
:
0.05
,
0.9
:
0.01
,
0.99
:
0.001
},
},
handlerFunc
,
)
...
...
@@ -245,34 +249,52 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo
},
instLabels
,
)
if
err
:=
Register
(
reqCnt
);
err
!=
nil
{
if
are
,
ok
:=
err
.
(
AlreadyRegisteredError
);
ok
{
reqCnt
=
are
.
ExistingCollector
.
(
*
CounterVec
)
}
else
{
panic
(
err
)
}
}
opts
.
Name
=
"request_duration_microseconds"
opts
.
Help
=
"The HTTP request latencies in microseconds."
reqDur
:=
NewSummary
(
opts
)
if
err
:=
Register
(
reqDur
);
err
!=
nil
{
if
are
,
ok
:=
err
.
(
AlreadyRegisteredError
);
ok
{
reqDur
=
are
.
ExistingCollector
.
(
Summary
)
}
else
{
panic
(
err
)
}
}
opts
.
Name
=
"request_size_bytes"
opts
.
Help
=
"The HTTP request sizes in bytes."
reqSz
:=
NewSummary
(
opts
)
if
err
:=
Register
(
reqSz
);
err
!=
nil
{
if
are
,
ok
:=
err
.
(
AlreadyRegisteredError
);
ok
{
reqSz
=
are
.
ExistingCollector
.
(
Summary
)
}
else
{
panic
(
err
)
}
}
opts
.
Name
=
"response_size_bytes"
opts
.
Help
=
"The HTTP response sizes in bytes."
resSz
:=
NewSummary
(
opts
)
regReqCnt
:=
MustRegisterOrGet
(
reqCnt
)
.
(
*
CounterVec
)
regReqDur
:=
MustRegisterOrGet
(
reqDur
)
.
(
Summary
)
regReqSz
:=
MustRegisterOrGet
(
reqSz
)
.
(
Summary
)
regResSz
:=
MustRegisterOrGet
(
resSz
)
.
(
Summary
)
if
err
:=
Register
(
resSz
);
err
!=
nil
{
if
are
,
ok
:=
err
.
(
AlreadyRegisteredError
);
ok
{
resSz
=
are
.
ExistingCollector
.
(
Summary
)
}
else
{
panic
(
err
)
}
}
return
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
now
:=
time
.
Now
()
delegate
:=
&
responseWriterDelegator
{
ResponseWriter
:
w
}
out
:=
make
(
chan
int
)
urlLen
:=
0
if
r
.
URL
!=
nil
{
urlLen
=
len
(
r
.
URL
.
String
())
}
go
computeApproximateRequestSize
(
r
,
out
,
urlLen
)
out
:=
computeApproximateRequestSize
(
r
)
_
,
cn
:=
w
.
(
http
.
CloseNotifier
)
_
,
fl
:=
w
.
(
http
.
Flusher
)
...
...
@@ -290,30 +312,44 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo
method
:=
sanitizeMethod
(
r
.
Method
)
code
:=
sanitizeCode
(
delegate
.
status
)
re
gRe
qCnt
.
WithLabelValues
(
method
,
code
)
.
Inc
()
re
gRe
qDur
.
Observe
(
elapsed
)
re
gRe
sSz
.
Observe
(
float64
(
delegate
.
written
))
re
gRe
qSz
.
Observe
(
float64
(
<-
out
))
reqCnt
.
WithLabelValues
(
method
,
code
)
.
Inc
()
reqDur
.
Observe
(
elapsed
)
resSz
.
Observe
(
float64
(
delegate
.
written
))
reqSz
.
Observe
(
float64
(
<-
out
))
})
}
func
computeApproximateRequestSize
(
r
*
http
.
Request
,
out
chan
int
,
s
int
)
{
s
+=
len
(
r
.
Method
)
s
+=
len
(
r
.
Proto
)
for
name
,
values
:=
range
r
.
Header
{
s
+=
len
(
name
)
for
_
,
value
:=
range
values
{
s
+=
len
(
value
)
}
func
computeApproximateRequestSize
(
r
*
http
.
Request
)
<-
chan
int
{
// Get URL length in current go routine for avoiding a race condition.
// HandlerFunc that runs in parallel may modify the URL.
s
:=
0
if
r
.
URL
!=
nil
{
s
+=
len
(
r
.
URL
.
String
())
}
s
+=
len
(
r
.
Host
)
// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
out
:=
make
(
chan
int
,
1
)
if
r
.
ContentLength
!=
-
1
{
s
+=
int
(
r
.
ContentLength
)
}
out
<-
s
go
func
()
{
s
+=
len
(
r
.
Method
)
s
+=
len
(
r
.
Proto
)
for
name
,
values
:=
range
r
.
Header
{
s
+=
len
(
name
)
for
_
,
value
:=
range
values
{
s
+=
len
(
value
)
}
}
s
+=
len
(
r
.
Host
)
// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
if
r
.
ContentLength
!=
-
1
{
s
+=
int
(
r
.
ContentLength
)
}
out
<-
s
close
(
out
)
}()
return
out
}
type
responseWriterDelegator
struct
{
...
...
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
浏览文件 @
1fb2190e
...
...
@@ -19,10 +19,10 @@ type processCollector struct {
pid
int
collectFn
func
(
chan
<-
Metric
)
pidFn
func
()
(
int
,
error
)
cpuTotal
Counter
openFDs
,
maxFDs
Gauge
vsize
,
rss
Gauge
startTime
Gauge
cpuTotal
*
Desc
openFDs
,
maxFDs
*
Desc
vsize
,
rss
*
Desc
startTime
*
Desc
}
// NewProcessCollector returns a collector which exports the current state of
...
...
@@ -44,40 +44,45 @@ func NewProcessCollectorPIDFn(
pidFn
func
()
(
int
,
error
),
namespace
string
,
)
Collector
{
ns
:=
""
if
len
(
namespace
)
>
0
{
ns
=
namespace
+
"_"
}
c
:=
processCollector
{
pidFn
:
pidFn
,
collectFn
:
func
(
chan
<-
Metric
)
{},
cpuTotal
:
New
Counter
(
CounterOpts
{
Namespace
:
namespace
,
Name
:
"process_cpu_seconds_total
"
,
Help
:
"Total user and system CPU time spent in seconds."
,
}
),
openFDs
:
New
Gauge
(
GaugeOpts
{
Namespace
:
namespace
,
Name
:
"process_open_fds
"
,
Help
:
"Number of open file descriptors."
,
}
),
maxFDs
:
New
Gauge
(
GaugeOpts
{
Namespace
:
namespace
,
Name
:
"process_max_fds
"
,
Help
:
"Maximum number of open file descriptors."
,
}
),
vsize
:
New
Gauge
(
GaugeOpts
{
Namespace
:
namespace
,
Name
:
"process_virtual_memory_bytes
"
,
Help
:
"Virtual memory size in bytes."
,
}
),
rss
:
New
Gauge
(
GaugeOpts
{
Namespace
:
namespace
,
Name
:
"process_resident_memory_bytes
"
,
Help
:
"Resident memory size in bytes."
,
}
),
startTime
:
New
Gauge
(
GaugeOpts
{
Namespace
:
namespace
,
Name
:
"process_start_time_seconds
"
,
Help
:
"Start time of the process since unix epoch in seconds."
,
}
),
cpuTotal
:
New
Desc
(
ns
+
"process_cpu_seconds_total"
,
"Total user and system CPU time spent in seconds.
"
,
nil
,
nil
,
),
openFDs
:
New
Desc
(
ns
+
"process_open_fds"
,
"Number of open file descriptors.
"
,
nil
,
nil
,
),
maxFDs
:
New
Desc
(
ns
+
"process_max_fds"
,
"Maximum number of open file descriptors.
"
,
nil
,
nil
,
),
vsize
:
New
Desc
(
ns
+
"process_virtual_memory_bytes"
,
"Virtual memory size in bytes.
"
,
nil
,
nil
,
),
rss
:
New
Desc
(
ns
+
"process_resident_memory_bytes"
,
"Resident memory size in bytes.
"
,
nil
,
nil
,
),
startTime
:
New
Desc
(
ns
+
"process_start_time_seconds"
,
"Start time of the process since unix epoch in seconds.
"
,
nil
,
nil
,
),
}
// Set up process metric collection if supported by the runtime.
...
...
@@ -90,12 +95,12 @@ func NewProcessCollectorPIDFn(
// Describe returns all descriptions of the collector.
func
(
c
*
processCollector
)
Describe
(
ch
chan
<-
*
Desc
)
{
ch
<-
c
.
cpuTotal
.
Desc
()
ch
<-
c
.
openFDs
.
Desc
()
ch
<-
c
.
maxFDs
.
Desc
()
ch
<-
c
.
vsize
.
Desc
()
ch
<-
c
.
rss
.
Desc
()
ch
<-
c
.
startTime
.
Desc
()
ch
<-
c
.
cpuTotal
ch
<-
c
.
openFDs
ch
<-
c
.
maxFDs
ch
<-
c
.
vsize
ch
<-
c
.
rss
ch
<-
c
.
startTime
}
// Collect returns the current state of all metrics of the collector.
...
...
@@ -117,26 +122,19 @@ func (c *processCollector) processCollect(ch chan<- Metric) {
}
if
stat
,
err
:=
p
.
NewStat
();
err
==
nil
{
c
.
cpuTotal
.
Set
(
stat
.
CPUTime
())
ch
<-
c
.
cpuTotal
c
.
vsize
.
Set
(
float64
(
stat
.
VirtualMemory
()))
ch
<-
c
.
vsize
c
.
rss
.
Set
(
float64
(
stat
.
ResidentMemory
()))
ch
<-
c
.
rss
ch
<-
MustNewConstMetric
(
c
.
cpuTotal
,
CounterValue
,
stat
.
CPUTime
())
ch
<-
MustNewConstMetric
(
c
.
vsize
,
GaugeValue
,
float64
(
stat
.
VirtualMemory
()))
ch
<-
MustNewConstMetric
(
c
.
rss
,
GaugeValue
,
float64
(
stat
.
ResidentMemory
()))
if
startTime
,
err
:=
stat
.
StartTime
();
err
==
nil
{
c
.
startTime
.
Set
(
startTime
)
ch
<-
c
.
startTime
ch
<-
MustNewConstMetric
(
c
.
startTime
,
GaugeValue
,
startTime
)
}
}
if
fds
,
err
:=
p
.
FileDescriptorsLen
();
err
==
nil
{
c
.
openFDs
.
Set
(
float64
(
fds
))
ch
<-
c
.
openFDs
ch
<-
MustNewConstMetric
(
c
.
openFDs
,
GaugeValue
,
float64
(
fds
))
}
if
limits
,
err
:=
p
.
NewLimits
();
err
==
nil
{
c
.
maxFDs
.
Set
(
float64
(
limits
.
OpenFiles
))
ch
<-
c
.
maxFDs
ch
<-
MustNewConstMetric
(
c
.
maxFDs
,
GaugeValue
,
float64
(
limits
.
OpenFiles
))
}
}
vendor/github.com/prometheus/client_golang/prometheus/registry.go
浏览文件 @
1fb2190e
...
...
@@ -152,38 +152,6 @@ func MustRegister(cs ...Collector) {
DefaultRegisterer
.
MustRegister
(
cs
...
)
}
// RegisterOrGet registers the provided Collector with the DefaultRegisterer and
// returns the Collector, unless an equal Collector was registered before, in
// which case that Collector is returned.
//
// Deprecated: RegisterOrGet is merely a convenience function for the
// implementation as described in the documentation for
// AlreadyRegisteredError. As the use case is relatively rare, this function
// will be removed in a future version of this package to clean up the
// namespace.
func
RegisterOrGet
(
c
Collector
)
(
Collector
,
error
)
{
if
err
:=
Register
(
c
);
err
!=
nil
{
if
are
,
ok
:=
err
.
(
AlreadyRegisteredError
);
ok
{
return
are
.
ExistingCollector
,
nil
}
return
nil
,
err
}
return
c
,
nil
}
// MustRegisterOrGet behaves like RegisterOrGet but panics instead of returning
// an error.
//
// Deprecated: This is deprecated for the same reason RegisterOrGet is. See
// there for details.
func
MustRegisterOrGet
(
c
Collector
)
Collector
{
c
,
err
:=
RegisterOrGet
(
c
)
if
err
!=
nil
{
panic
(
err
)
}
return
c
}
// Unregister removes the registration of the provided Collector from the
// DefaultRegisterer.
//
...
...
@@ -201,25 +169,6 @@ func (gf GathererFunc) Gather() ([]*dto.MetricFamily, error) {
return
gf
()
}
// SetMetricFamilyInjectionHook replaces the DefaultGatherer with one that
// gathers from the previous DefaultGatherers but then merges the MetricFamily
// protobufs returned from the provided hook function with the MetricFamily
// protobufs returned from the original DefaultGatherer.
//
// Deprecated: This function manipulates the DefaultGatherer variable. Consider
// the implications, i.e. don't do this concurrently with any uses of the
// DefaultGatherer. In the rare cases where you need to inject MetricFamily
// protobufs directly, it is recommended to use a custom Registry and combine it
// with a custom Gatherer using the Gatherers type (see
// there). SetMetricFamilyInjectionHook only exists for compatibility reasons
// with previous versions of this package.
func
SetMetricFamilyInjectionHook
(
hook
func
()
[]
*
dto
.
MetricFamily
)
{
DefaultGatherer
=
Gatherers
{
DefaultGatherer
,
GathererFunc
(
func
()
([]
*
dto
.
MetricFamily
,
error
)
{
return
hook
(),
nil
}),
}
}
// AlreadyRegisteredError is returned by the Register method if the Collector to
// be registered has already been registered before, or a different Collector
// that collects the same metrics has been registered before. Registration fails
...
...
@@ -294,7 +243,7 @@ func (r *Registry) Register(c Collector) error {
}()
r
.
mtx
.
Lock
()
defer
r
.
mtx
.
Unlock
()
// Coduct various tests...
// Co
n
duct various tests...
for
desc
:=
range
descChan
{
// Is the descriptor valid at all?
...
...
@@ -447,7 +396,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
// Drain metricChan in case of premature return.
defer
func
()
{
for
_
=
range
metricChan
{
for
range
metricChan
{
}
}()
...
...
@@ -683,7 +632,7 @@ func (s metricSorter) Less(i, j int) bool {
return
s
[
i
]
.
GetTimestampMs
()
<
s
[
j
]
.
GetTimestampMs
()
}
// normalizeMetricFamilies returns a MetricFamily slice w
h
ith empty
// normalizeMetricFamilies returns a MetricFamily slice with empty
// MetricFamilies pruned and the remaining MetricFamilies sorted by name within
// the slice, with the contained Metrics sorted within each MetricFamily.
func
normalizeMetricFamilies
(
metricFamiliesByName
map
[
string
]
*
dto
.
MetricFamily
)
[]
*
dto
.
MetricFamily
{
...
...
vendor/github.com/prometheus/client_golang/prometheus/summary.go
浏览文件 @
1fb2190e
...
...
@@ -54,6 +54,9 @@ type Summary interface {
}
// DefObjectives are the default Summary quantile values.
//
// Deprecated: DefObjectives will not be used as the default objectives in
// v0.10 of the library. The default Summary will have no quantiles then.
var
(
DefObjectives
=
map
[
float64
]
float64
{
0.5
:
0.05
,
0.9
:
0.01
,
0.99
:
0.001
}
...
...
@@ -113,9 +116,15 @@ type SummaryOpts struct {
ConstLabels
Labels
// Objectives defines the quantile rank estimates with their respective
// absolute error. If Objectives[q] = e, then the value reported
// for q will be the φ-quantile value for some φ between q-e and q+e.
// The default value is DefObjectives.
// absolute error. If Objectives[q] = e, then the value reported for q
// will be the φ-quantile value for some φ between q-e and q+e. The
// default value is DefObjectives. It is used if Objectives is left at
// its zero value (i.e. nil). To create a Summary without Objectives,
// set it to an empty map (i.e. map[float64]float64{}).
//
// Deprecated: Note that the current value of DefObjectives is
// deprecated. It will be replaced by an empty map in v0.10 of the
// library. Please explicitly set Objectives to the desired value.
Objectives
map
[
float64
]
float64
// MaxAge defines the duration for which an observation stays relevant
...
...
@@ -183,7 +192,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
}
}
if
len
(
opts
.
Objectives
)
==
0
{
if
opts
.
Objectives
==
nil
{
opts
.
Objectives
=
DefObjectives
}
...
...
vendor/github.com/prometheus/client_golang/prometheus/timer.go
0 → 100644
浏览文件 @
1fb2190e
// Copyright 2016 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.
package
prometheus
import
"time"
// Observer is the interface that wraps the Observe method, which is used by
// Histogram and Summary to add observations.
type
Observer
interface
{
Observe
(
float64
)
}
// The ObserverFunc type is an adapter to allow the use of ordinary
// functions as Observers. If f is a function with the appropriate
// signature, ObserverFunc(f) is an Observer that calls f.
//
// This adapter is usually used in connection with the Timer type, and there are
// two general use cases:
//
// The most common one is to use a Gauge as the Observer for a Timer.
// See the "Gauge" Timer example.
//
// The more advanced use case is to create a function that dynamically decides
// which Observer to use for observing the duration. See the "Complex" Timer
// example.
type
ObserverFunc
func
(
float64
)
// Observe calls f(value). It implements Observer.
func
(
f
ObserverFunc
)
Observe
(
value
float64
)
{
f
(
value
)
}
// Timer is a helper type to time functions. Use NewTimer to create new
// instances.
type
Timer
struct
{
begin
time
.
Time
observer
Observer
}
// NewTimer creates a new Timer. The provided Observer is used to observe a
// duration in seconds. Timer is usually used to time a function call in the
// following way:
// func TimeMe() {
// timer := NewTimer(myHistogram)
// defer timer.ObserveDuration()
// // Do actual work.
// }
func
NewTimer
(
o
Observer
)
*
Timer
{
return
&
Timer
{
begin
:
time
.
Now
(),
observer
:
o
,
}
}
// ObserveDuration records the duration passed since the Timer was created with
// NewTimer. It calls the Observe method of the Observer provided during
// construction with the duration in seconds as an argument. ObserveDuration is
// usually called with a defer statement.
func
(
t
*
Timer
)
ObserveDuration
()
{
if
t
.
observer
!=
nil
{
t
.
observer
.
Observe
(
time
.
Since
(
t
.
begin
)
.
Seconds
())
}
}
vendor/github.com/prometheus/client_golang/prometheus/untyped.go
浏览文件 @
1fb2190e
...
...
@@ -20,6 +20,11 @@ package prometheus
// no type information is implied.
//
// To create Untyped instances, use NewUntyped.
//
// Deprecated: The Untyped type is deprecated because it doesn't make sense in
// direct instrumentation. If you need to mirror an external metric of unknown
// type (usually while writing exporters), Use MustNewConstMetric to create an
// untyped metric instance on the fly.
type
Untyped
interface
{
Metric
Collector
...
...
vendor/github.com/prometheus/client_golang/prometheus/value.go
浏览文件 @
1fb2190e
...
...
@@ -19,6 +19,7 @@ import (
"math"
"sort"
"sync/atomic"
"time"
dto
"github.com/prometheus/client_model/go"
...
...
@@ -43,7 +44,7 @@ var errInconsistentCardinality = errors.New("inconsistent label cardinality")
// ValueType. This is a low-level building block used by the library to back the
// implementations of Counter, Gauge, and Untyped.
type
value
struct
{
// valBits contains
t
the bits of the represented float64 value. It has
// valBits contains the bits of the represented float64 value. It has
// to go first in the struct to guarantee alignment for atomic
// operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG
valBits
uint64
...
...
@@ -80,6 +81,10 @@ func (v *value) Set(val float64) {
atomic
.
StoreUint64
(
&
v
.
valBits
,
math
.
Float64bits
(
val
))
}
func
(
v
*
value
)
SetToCurrentTime
()
{
v
.
Set
(
float64
(
time
.
Now
()
.
UnixNano
())
/
1e9
)
}
func
(
v
*
value
)
Inc
()
{
v
.
Add
(
1
)
}
...
...
vendor/github.com/vaughan0/go-ini/LICENSE
已删除
100644 → 0
浏览文件 @
0e0fc5a7
Copyright (c) 2013 Vaughan Newton
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.
vendor/github.com/vaughan0/go-ini/README.md
已删除
100644 → 0
浏览文件 @
0e0fc5a7
go-ini
======
INI parsing library for Go (golang).
View the API documentation
[
here
](
http://godoc.org/github.com/vaughan0/go-ini
)
.
Usage
-----
Parse an INI file:
```
go
import
"github.com/vaughan0/go-ini"
file
,
err
:=
ini
.
LoadFile
(
"myfile.ini"
)
```
Get data from the parsed file:
```
go
name
,
ok
:=
file
.
Get
(
"person"
,
"name"
)
if
!
ok
{
panic
(
"'name' variable missing from 'person' section"
)
}
```
Iterate through values in a section:
```
go
for
key
,
value
:=
range
file
[
"mysection"
]
{
fmt
.
Printf
(
"%s => %s
\n
"
,
key
,
value
)
}
```
Iterate through sections in a file:
```
go
for
name
,
section
:=
range
file
{
fmt
.
Printf
(
"Section name: %s
\n
"
,
name
)
}
```
File Format
-----------
INI files are parsed by go-ini line-by-line. Each line may be one of the following:
*
A section definition: [section-name]
*
A property: key = value
*
A comment: #blahblah _or_ ;blahblah
*
Blank. The line will be ignored.
Properties defined before any section headers are placed in the default section, which has
the empty string as it's key.
Example:
```
ini
# I am a comment
; So am I!
[apples]
colour
=
red or green
shape
=
applish
[oranges]
shape
=
square
colour
=
blue
```
vendor/github.com/vaughan0/go-ini/ini.go
已删除
100644 → 0
浏览文件 @
0e0fc5a7
// Package ini provides functions for parsing INI configuration files.
package
ini
import
(
"bufio"
"fmt"
"io"
"os"
"regexp"
"strings"
)
var
(
sectionRegex
=
regexp
.
MustCompile
(
`^\[(.*)\]$`
)
assignRegex
=
regexp
.
MustCompile
(
`^([^=]+)=(.*)$`
)
)
// ErrSyntax is returned when there is a syntax error in an INI file.
type
ErrSyntax
struct
{
Line
int
Source
string
// The contents of the erroneous line, without leading or trailing whitespace
}
func
(
e
ErrSyntax
)
Error
()
string
{
return
fmt
.
Sprintf
(
"invalid INI syntax on line %d: %s"
,
e
.
Line
,
e
.
Source
)
}
// A File represents a parsed INI file.
type
File
map
[
string
]
Section
// A Section represents a single section of an INI file.
type
Section
map
[
string
]
string
// Returns a named Section. A Section will be created if one does not already exist for the given name.
func
(
f
File
)
Section
(
name
string
)
Section
{
section
:=
f
[
name
]
if
section
==
nil
{
section
=
make
(
Section
)
f
[
name
]
=
section
}
return
section
}
// Looks up a value for a key in a section and returns that value, along with a boolean result similar to a map lookup.
func
(
f
File
)
Get
(
section
,
key
string
)
(
value
string
,
ok
bool
)
{
if
s
:=
f
[
section
];
s
!=
nil
{
value
,
ok
=
s
[
key
]
}
return
}
// Loads INI data from a reader and stores the data in the File.
func
(
f
File
)
Load
(
in
io
.
Reader
)
(
err
error
)
{
bufin
,
ok
:=
in
.
(
*
bufio
.
Reader
)
if
!
ok
{
bufin
=
bufio
.
NewReader
(
in
)
}
return
parseFile
(
bufin
,
f
)
}
// Loads INI data from a named file and stores the data in the File.
func
(
f
File
)
LoadFile
(
file
string
)
(
err
error
)
{
in
,
err
:=
os
.
Open
(
file
)
if
err
!=
nil
{
return
}
defer
in
.
Close
()
return
f
.
Load
(
in
)
}
func
parseFile
(
in
*
bufio
.
Reader
,
file
File
)
(
err
error
)
{
section
:=
""
lineNum
:=
0
for
done
:=
false
;
!
done
;
{
var
line
string
if
line
,
err
=
in
.
ReadString
(
'\n'
);
err
!=
nil
{
if
err
==
io
.
EOF
{
done
=
true
}
else
{
return
}
}
lineNum
++
line
=
strings
.
TrimSpace
(
line
)
if
len
(
line
)
==
0
{
// Skip blank lines
continue
}
if
line
[
0
]
==
';'
||
line
[
0
]
==
'#'
{
// Skip comments
continue
}
if
groups
:=
assignRegex
.
FindStringSubmatch
(
line
);
groups
!=
nil
{
key
,
val
:=
groups
[
1
],
groups
[
2
]
key
,
val
=
strings
.
TrimSpace
(
key
),
strings
.
TrimSpace
(
val
)
file
.
Section
(
section
)[
key
]
=
val
}
else
if
groups
:=
sectionRegex
.
FindStringSubmatch
(
line
);
groups
!=
nil
{
name
:=
strings
.
TrimSpace
(
groups
[
1
])
section
=
name
// Create the section if it does not exist
file
.
Section
(
section
)
}
else
{
return
ErrSyntax
{
lineNum
,
line
}
}
}
return
nil
}
// Loads and returns a File from a reader.
func
Load
(
in
io
.
Reader
)
(
File
,
error
)
{
file
:=
make
(
File
)
err
:=
file
.
Load
(
in
)
return
file
,
err
}
// Loads and returns an INI File from a file on disk.
func
LoadFile
(
filename
string
)
(
File
,
error
)
{
file
:=
make
(
File
)
err
:=
file
.
LoadFile
(
filename
)
return
file
,
err
}
vendor/github.com/vaughan0/go-ini/test.ini
已删除
100644 → 0
浏览文件 @
0e0fc5a7
[default]
stuff
=
things
vendor/golang.org/x/net/internal/timeseries/timeseries.go
已删除
100644 → 0
浏览文件 @
0e0fc5a7
// Copyright 2015 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 timeseries implements a time series structure for stats collection.
package
timeseries
// import "golang.org/x/net/internal/timeseries"
import
(
"fmt"
"log"
"time"
)
const
(
timeSeriesNumBuckets
=
64
minuteHourSeriesNumBuckets
=
60
)
var
timeSeriesResolutions
=
[]
time
.
Duration
{
1
*
time
.
Second
,
10
*
time
.
Second
,
1
*
time
.
Minute
,
10
*
time
.
Minute
,
1
*
time
.
Hour
,
6
*
time
.
Hour
,
24
*
time
.
Hour
,
// 1 day
7
*
24
*
time
.
Hour
,
// 1 week
4
*
7
*
24
*
time
.
Hour
,
// 4 weeks
16
*
7
*
24
*
time
.
Hour
,
// 16 weeks
}
var
minuteHourSeriesResolutions
=
[]
time
.
Duration
{
1
*
time
.
Second
,
1
*
time
.
Minute
,
}
// An Observable is a kind of data that can be aggregated in a time series.
type
Observable
interface
{
Multiply
(
ratio
float64
)
// Multiplies the data in self by a given ratio
Add
(
other
Observable
)
// Adds the data from a different observation to self
Clear
()
// Clears the observation so it can be reused.
CopyFrom
(
other
Observable
)
// Copies the contents of a given observation to self
}
// Float attaches the methods of Observable to a float64.
type
Float
float64
// NewFloat returns a Float.
func
NewFloat
()
Observable
{
f
:=
Float
(
0
)
return
&
f
}
// String returns the float as a string.
func
(
f
*
Float
)
String
()
string
{
return
fmt
.
Sprintf
(
"%g"
,
f
.
Value
())
}
// Value returns the float's value.
func
(
f
*
Float
)
Value
()
float64
{
return
float64
(
*
f
)
}
func
(
f
*
Float
)
Multiply
(
ratio
float64
)
{
*
f
*=
Float
(
ratio
)
}
func
(
f
*
Float
)
Add
(
other
Observable
)
{
o
:=
other
.
(
*
Float
)
*
f
+=
*
o
}
func
(
f
*
Float
)
Clear
()
{
*
f
=
0
}
func
(
f
*
Float
)
CopyFrom
(
other
Observable
)
{
o
:=
other
.
(
*
Float
)
*
f
=
*
o
}
// A Clock tells the current time.
type
Clock
interface
{
Time
()
time
.
Time
}
type
defaultClock
int
var
defaultClockInstance
defaultClock
func
(
defaultClock
)
Time
()
time
.
Time
{
return
time
.
Now
()
}
// Information kept per level. Each level consists of a circular list of
// observations. The start of the level may be derived from end and the
// len(buckets) * sizeInMillis.
type
tsLevel
struct
{
oldest
int
// index to oldest bucketed Observable
newest
int
// index to newest bucketed Observable
end
time
.
Time
// end timestamp for this level
size
time
.
Duration
// duration of the bucketed Observable
buckets
[]
Observable
// collections of observations
provider
func
()
Observable
// used for creating new Observable
}
func
(
l
*
tsLevel
)
Clear
()
{
l
.
oldest
=
0
l
.
newest
=
len
(
l
.
buckets
)
-
1
l
.
end
=
time
.
Time
{}
for
i
:=
range
l
.
buckets
{
if
l
.
buckets
[
i
]
!=
nil
{
l
.
buckets
[
i
]
.
Clear
()
l
.
buckets
[
i
]
=
nil
}
}
}
func
(
l
*
tsLevel
)
InitLevel
(
size
time
.
Duration
,
numBuckets
int
,
f
func
()
Observable
)
{
l
.
size
=
size
l
.
provider
=
f
l
.
buckets
=
make
([]
Observable
,
numBuckets
)
}
// Keeps a sequence of levels. Each level is responsible for storing data at
// a given resolution. For example, the first level stores data at a one
// minute resolution while the second level stores data at a one hour
// resolution.
// Each level is represented by a sequence of buckets. Each bucket spans an
// interval equal to the resolution of the level. New observations are added
// to the last bucket.
type
timeSeries
struct
{
provider
func
()
Observable
// make more Observable
numBuckets
int
// number of buckets in each level
levels
[]
*
tsLevel
// levels of bucketed Observable
lastAdd
time
.
Time
// time of last Observable tracked
total
Observable
// convenient aggregation of all Observable
clock
Clock
// Clock for getting current time
pending
Observable
// observations not yet bucketed
pendingTime
time
.
Time
// what time are we keeping in pending
dirty
bool
// if there are pending observations
}
// init initializes a level according to the supplied criteria.
func
(
ts
*
timeSeries
)
init
(
resolutions
[]
time
.
Duration
,
f
func
()
Observable
,
numBuckets
int
,
clock
Clock
)
{
ts
.
provider
=
f
ts
.
numBuckets
=
numBuckets
ts
.
clock
=
clock
ts
.
levels
=
make
([]
*
tsLevel
,
len
(
resolutions
))
for
i
:=
range
resolutions
{
if
i
>
0
&&
resolutions
[
i
-
1
]
>=
resolutions
[
i
]
{
log
.
Print
(
"timeseries: resolutions must be monotonically increasing"
)
break
}
newLevel
:=
new
(
tsLevel
)
newLevel
.
InitLevel
(
resolutions
[
i
],
ts
.
numBuckets
,
ts
.
provider
)
ts
.
levels
[
i
]
=
newLevel
}
ts
.
Clear
()
}
// Clear removes all observations from the time series.
func
(
ts
*
timeSeries
)
Clear
()
{
ts
.
lastAdd
=
time
.
Time
{}
ts
.
total
=
ts
.
resetObservation
(
ts
.
total
)
ts
.
pending
=
ts
.
resetObservation
(
ts
.
pending
)
ts
.
pendingTime
=
time
.
Time
{}
ts
.
dirty
=
false
for
i
:=
range
ts
.
levels
{
ts
.
levels
[
i
]
.
Clear
()
}
}
// Add records an observation at the current time.
func
(
ts
*
timeSeries
)
Add
(
observation
Observable
)
{
ts
.
AddWithTime
(
observation
,
ts
.
clock
.
Time
())
}
// AddWithTime records an observation at the specified time.
func
(
ts
*
timeSeries
)
AddWithTime
(
observation
Observable
,
t
time
.
Time
)
{
smallBucketDuration
:=
ts
.
levels
[
0
]
.
size
if
t
.
After
(
ts
.
lastAdd
)
{
ts
.
lastAdd
=
t
}
if
t
.
After
(
ts
.
pendingTime
)
{
ts
.
advance
(
t
)
ts
.
mergePendingUpdates
()
ts
.
pendingTime
=
ts
.
levels
[
0
]
.
end
ts
.
pending
.
CopyFrom
(
observation
)
ts
.
dirty
=
true
}
else
if
t
.
After
(
ts
.
pendingTime
.
Add
(
-
1
*
smallBucketDuration
))
{
// The observation is close enough to go into the pending bucket.
// This compensates for clock skewing and small scheduling delays
// by letting the update stay in the fast path.
ts
.
pending
.
Add
(
observation
)
ts
.
dirty
=
true
}
else
{
ts
.
mergeValue
(
observation
,
t
)
}
}
// mergeValue inserts the observation at the specified time in the past into all levels.
func
(
ts
*
timeSeries
)
mergeValue
(
observation
Observable
,
t
time
.
Time
)
{
for
_
,
level
:=
range
ts
.
levels
{
index
:=
(
ts
.
numBuckets
-
1
)
-
int
(
level
.
end
.
Sub
(
t
)
/
level
.
size
)
if
0
<=
index
&&
index
<
ts
.
numBuckets
{
bucketNumber
:=
(
level
.
oldest
+
index
)
%
ts
.
numBuckets
if
level
.
buckets
[
bucketNumber
]
==
nil
{
level
.
buckets
[
bucketNumber
]
=
level
.
provider
()
}
level
.
buckets
[
bucketNumber
]
.
Add
(
observation
)
}
}
ts
.
total
.
Add
(
observation
)
}
// mergePendingUpdates applies the pending updates into all levels.
func
(
ts
*
timeSeries
)
mergePendingUpdates
()
{
if
ts
.
dirty
{
ts
.
mergeValue
(
ts
.
pending
,
ts
.
pendingTime
)
ts
.
pending
=
ts
.
resetObservation
(
ts
.
pending
)
ts
.
dirty
=
false
}
}
// advance cycles the buckets at each level until the latest bucket in
// each level can hold the time specified.
func
(
ts
*
timeSeries
)
advance
(
t
time
.
Time
)
{
if
!
t
.
After
(
ts
.
levels
[
0
]
.
end
)
{
return
}
for
i
:=
0
;
i
<
len
(
ts
.
levels
);
i
++
{
level
:=
ts
.
levels
[
i
]
if
!
level
.
end
.
Before
(
t
)
{
break
}
// If the time is sufficiently far, just clear the level and advance
// directly.
if
!
t
.
Before
(
level
.
end
.
Add
(
level
.
size
*
time
.
Duration
(
ts
.
numBuckets
)))
{
for
_
,
b
:=
range
level
.
buckets
{
ts
.
resetObservation
(
b
)
}
level
.
end
=
time
.
Unix
(
0
,
(
t
.
UnixNano
()
/
level
.
size
.
Nanoseconds
())
*
level
.
size
.
Nanoseconds
())
}
for
t
.
After
(
level
.
end
)
{
level
.
end
=
level
.
end
.
Add
(
level
.
size
)
level
.
newest
=
level
.
oldest
level
.
oldest
=
(
level
.
oldest
+
1
)
%
ts
.
numBuckets
ts
.
resetObservation
(
level
.
buckets
[
level
.
newest
])
}
t
=
level
.
end
}
}
// Latest returns the sum of the num latest buckets from the level.
func
(
ts
*
timeSeries
)
Latest
(
level
,
num
int
)
Observable
{
now
:=
ts
.
clock
.
Time
()
if
ts
.
levels
[
0
]
.
end
.
Before
(
now
)
{
ts
.
advance
(
now
)
}
ts
.
mergePendingUpdates
()
result
:=
ts
.
provider
()
l
:=
ts
.
levels
[
level
]
index
:=
l
.
newest
for
i
:=
0
;
i
<
num
;
i
++
{
if
l
.
buckets
[
index
]
!=
nil
{
result
.
Add
(
l
.
buckets
[
index
])
}
if
index
==
0
{
index
=
ts
.
numBuckets
}
index
--
}
return
result
}
// LatestBuckets returns a copy of the num latest buckets from level.
func
(
ts
*
timeSeries
)
LatestBuckets
(
level
,
num
int
)
[]
Observable
{
if
level
<
0
||
level
>
len
(
ts
.
levels
)
{
log
.
Print
(
"timeseries: bad level argument: "
,
level
)
return
nil
}
if
num
<
0
||
num
>=
ts
.
numBuckets
{
log
.
Print
(
"timeseries: bad num argument: "
,
num
)
return
nil
}
results
:=
make
([]
Observable
,
num
)
now
:=
ts
.
clock
.
Time
()
if
ts
.
levels
[
0
]
.
end
.
Before
(
now
)
{
ts
.
advance
(
now
)
}
ts
.
mergePendingUpdates
()
l
:=
ts
.
levels
[
level
]
index
:=
l
.
newest
for
i
:=
0
;
i
<
num
;
i
++
{
result
:=
ts
.
provider
()
results
[
i
]
=
result
if
l
.
buckets
[
index
]
!=
nil
{
result
.
CopyFrom
(
l
.
buckets
[
index
])
}
if
index
==
0
{
index
=
ts
.
numBuckets
}
index
-=
1
}
return
results
}
// ScaleBy updates observations by scaling by factor.
func
(
ts
*
timeSeries
)
ScaleBy
(
factor
float64
)
{
for
_
,
l
:=
range
ts
.
levels
{
for
i
:=
0
;
i
<
ts
.
numBuckets
;
i
++
{
l
.
buckets
[
i
]
.
Multiply
(
factor
)
}
}
ts
.
total
.
Multiply
(
factor
)
ts
.
pending
.
Multiply
(
factor
)
}
// Range returns the sum of observations added over the specified time range.
// If start or finish times don't fall on bucket boundaries of the same
// level, then return values are approximate answers.
func
(
ts
*
timeSeries
)
Range
(
start
,
finish
time
.
Time
)
Observable
{
return
ts
.
ComputeRange
(
start
,
finish
,
1
)[
0
]
}
// Recent returns the sum of observations from the last delta.
func
(
ts
*
timeSeries
)
Recent
(
delta
time
.
Duration
)
Observable
{
now
:=
ts
.
clock
.
Time
()
return
ts
.
Range
(
now
.
Add
(
-
delta
),
now
)
}
// Total returns the total of all observations.
func
(
ts
*
timeSeries
)
Total
()
Observable
{
ts
.
mergePendingUpdates
()
return
ts
.
total
}
// ComputeRange computes a specified number of values into a slice using
// the observations recorded over the specified time period. The return
// values are approximate if the start or finish times don't fall on the
// bucket boundaries at the same level or if the number of buckets spanning
// the range is not an integral multiple of num.
func
(
ts
*
timeSeries
)
ComputeRange
(
start
,
finish
time
.
Time
,
num
int
)
[]
Observable
{
if
start
.
After
(
finish
)
{
log
.
Printf
(
"timeseries: start > finish, %v>%v"
,
start
,
finish
)
return
nil
}
if
num
<
0
{
log
.
Printf
(
"timeseries: num < 0, %v"
,
num
)
return
nil
}
results
:=
make
([]
Observable
,
num
)
for
_
,
l
:=
range
ts
.
levels
{
if
!
start
.
Before
(
l
.
end
.
Add
(
-
l
.
size
*
time
.
Duration
(
ts
.
numBuckets
)))
{
ts
.
extract
(
l
,
start
,
finish
,
num
,
results
)
return
results
}
}
// Failed to find a level that covers the desired range. So just
// extract from the last level, even if it doesn't cover the entire
// desired range.
ts
.
extract
(
ts
.
levels
[
len
(
ts
.
levels
)
-
1
],
start
,
finish
,
num
,
results
)
return
results
}
// RecentList returns the specified number of values in slice over the most
// recent time period of the specified range.
func
(
ts
*
timeSeries
)
RecentList
(
delta
time
.
Duration
,
num
int
)
[]
Observable
{
if
delta
<
0
{
return
nil
}
now
:=
ts
.
clock
.
Time
()
return
ts
.
ComputeRange
(
now
.
Add
(
-
delta
),
now
,
num
)
}
// extract returns a slice of specified number of observations from a given
// level over a given range.
func
(
ts
*
timeSeries
)
extract
(
l
*
tsLevel
,
start
,
finish
time
.
Time
,
num
int
,
results
[]
Observable
)
{
ts
.
mergePendingUpdates
()
srcInterval
:=
l
.
size
dstInterval
:=
finish
.
Sub
(
start
)
/
time
.
Duration
(
num
)
dstStart
:=
start
srcStart
:=
l
.
end
.
Add
(
-
srcInterval
*
time
.
Duration
(
ts
.
numBuckets
))
srcIndex
:=
0
// Where should scanning start?
if
dstStart
.
After
(
srcStart
)
{
advance
:=
dstStart
.
Sub
(
srcStart
)
/
srcInterval
srcIndex
+=
int
(
advance
)
srcStart
=
srcStart
.
Add
(
advance
*
srcInterval
)
}
// The i'th value is computed as show below.
// interval = (finish/start)/num
// i'th value = sum of observation in range
// [ start + i * interval,
// start + (i + 1) * interval )
for
i
:=
0
;
i
<
num
;
i
++
{
results
[
i
]
=
ts
.
resetObservation
(
results
[
i
])
dstEnd
:=
dstStart
.
Add
(
dstInterval
)
for
srcIndex
<
ts
.
numBuckets
&&
srcStart
.
Before
(
dstEnd
)
{
srcEnd
:=
srcStart
.
Add
(
srcInterval
)
if
srcEnd
.
After
(
ts
.
lastAdd
)
{
srcEnd
=
ts
.
lastAdd
}
if
!
srcEnd
.
Before
(
dstStart
)
{
srcValue
:=
l
.
buckets
[(
srcIndex
+
l
.
oldest
)
%
ts
.
numBuckets
]
if
!
srcStart
.
Before
(
dstStart
)
&&
!
srcEnd
.
After
(
dstEnd
)
{
// dst completely contains src.
if
srcValue
!=
nil
{
results
[
i
]
.
Add
(
srcValue
)
}
}
else
{
// dst partially overlaps src.
overlapStart
:=
maxTime
(
srcStart
,
dstStart
)
overlapEnd
:=
minTime
(
srcEnd
,
dstEnd
)
base
:=
srcEnd
.
Sub
(
srcStart
)
fraction
:=
overlapEnd
.
Sub
(
overlapStart
)
.
Seconds
()
/
base
.
Seconds
()
used
:=
ts
.
provider
()
if
srcValue
!=
nil
{
used
.
CopyFrom
(
srcValue
)
}
used
.
Multiply
(
fraction
)
results
[
i
]
.
Add
(
used
)
}
if
srcEnd
.
After
(
dstEnd
)
{
break
}
}
srcIndex
++
srcStart
=
srcStart
.
Add
(
srcInterval
)
}
dstStart
=
dstStart
.
Add
(
dstInterval
)
}
}
// resetObservation clears the content so the struct may be reused.
func
(
ts
*
timeSeries
)
resetObservation
(
observation
Observable
)
Observable
{
if
observation
==
nil
{
observation
=
ts
.
provider
()
}
else
{
observation
.
Clear
()
}
return
observation
}
// TimeSeries tracks data at granularities from 1 second to 16 weeks.
type
TimeSeries
struct
{
timeSeries
}
// NewTimeSeries creates a new TimeSeries using the function provided for creating new Observable.
func
NewTimeSeries
(
f
func
()
Observable
)
*
TimeSeries
{
return
NewTimeSeriesWithClock
(
f
,
defaultClockInstance
)
}
// NewTimeSeriesWithClock creates a new TimeSeries using the function provided for creating new Observable and the clock for
// assigning timestamps.
func
NewTimeSeriesWithClock
(
f
func
()
Observable
,
clock
Clock
)
*
TimeSeries
{
ts
:=
new
(
TimeSeries
)
ts
.
timeSeries
.
init
(
timeSeriesResolutions
,
f
,
timeSeriesNumBuckets
,
clock
)
return
ts
}
// MinuteHourSeries tracks data at granularities of 1 minute and 1 hour.
type
MinuteHourSeries
struct
{
timeSeries
}
// NewMinuteHourSeries creates a new MinuteHourSeries using the function provided for creating new Observable.
func
NewMinuteHourSeries
(
f
func
()
Observable
)
*
MinuteHourSeries
{
return
NewMinuteHourSeriesWithClock
(
f
,
defaultClockInstance
)
}
// NewMinuteHourSeriesWithClock creates a new MinuteHourSeries using the function provided for creating new Observable and the clock for
// assigning timestamps.
func
NewMinuteHourSeriesWithClock
(
f
func
()
Observable
,
clock
Clock
)
*
MinuteHourSeries
{
ts
:=
new
(
MinuteHourSeries
)
ts
.
timeSeries
.
init
(
minuteHourSeriesResolutions
,
f
,
minuteHourSeriesNumBuckets
,
clock
)
return
ts
}
func
(
ts
*
MinuteHourSeries
)
Minute
()
Observable
{
return
ts
.
timeSeries
.
Latest
(
0
,
60
)
}
func
(
ts
*
MinuteHourSeries
)
Hour
()
Observable
{
return
ts
.
timeSeries
.
Latest
(
1
,
60
)
}
func
minTime
(
a
,
b
time
.
Time
)
time
.
Time
{
if
a
.
Before
(
b
)
{
return
a
}
return
b
}
func
maxTime
(
a
,
b
time
.
Time
)
time
.
Time
{
if
a
.
After
(
b
)
{
return
a
}
return
b
}
vendor/vendor.json
浏览文件 @
1fb2190e
...
...
@@ -2,20 +2,6 @@
"comment"
:
""
,
"ignore"
:
"test"
,
"package"
:
[
{
"checksumSHA1"
:
"Cslv4/ITyQmgjSUhNXFu8q5bqOU="
,
"origin"
:
"k8s.io/client-go/1.5/vendor/cloud.google.com/go/compute/metadata"
,
"path"
:
"cloud.google.com/go/compute/metadata"
,
"revision"
:
"c589d0c9f0d81640c518354c7bcae77d99820aa3"
,
"revisionTime"
:
"2016-09-30T00:14:02Z"
},
{
"checksumSHA1"
:
"hiJXjkFEGy+sDFf6O58Ocdy9Rnk="
,
"origin"
:
"k8s.io/client-go/1.5/vendor/cloud.google.com/go/internal"
,
"path"
:
"cloud.google.com/go/internal"
,
"revision"
:
"c589d0c9f0d81640c518354c7bcae77d99820aa3"
,
"revisionTime"
:
"2016-09-30T00:14:02Z"
},
{
"checksumSHA1"
:
"oIt4tXgFYnZJBsCac1BQLnTWALM="
,
"path"
:
"github.com/Azure/azure-sdk-for-go/arm/compute"
,
...
...
@@ -533,10 +519,10 @@
"revisionTime"
:
"2016-06-15T09:26:46Z"
},
{
"checksumSHA1"
:
"
KkB+77Ziom7N6RzSbyUwYGrmDeU
="
,
"checksumSHA1"
:
"
d2irkxoHgazkTuLIvJGiYwagl8o
="
,
"path"
:
"github.com/prometheus/client_golang/prometheus"
,
"revision"
:
"
c5b7fccd204277076155f10851dad72b76a49317
"
,
"revisionTime"
:
"201
6-08-17T15:48:24
Z"
"revision"
:
"
08fd2e12372a66e68e30523c7642e0cbc3e4fbde
"
,
"revisionTime"
:
"201
7-04-01T10:34:46
Z"
},
{
"checksumSHA1"
:
"DvwvOlPNAgRntBzt3b3OSRMS2N4="
,
...
...
@@ -545,10 +531,10 @@
"revisionTime"
:
"2015-02-12T10:17:44Z"
},
{
"checksumSHA1"
:
"
jG8qYuDUuaZeflt4JxBBdyQBsXw
="
,
"checksumSHA1"
:
"
Wtpzndm/+bdwwNU5PCTfb4oUhc8
="
,
"path"
:
"github.com/prometheus/common/expfmt"
,
"revision"
:
"
dd2f054febf4a6c00f2343686efb775948a8bff4
"
,
"revisionTime"
:
"2017-0
1-08T23:12:12
Z"
"revision"
:
"
49fee292b27bfff7f354ee0f64e1bc4850462edf
"
,
"revisionTime"
:
"2017-0
2-20T10:38:46
Z"
},
{
"checksumSHA1"
:
"GWlM3d2vPYyNATtTFgftS10/A9w="
,
...
...
@@ -563,10 +549,10 @@
"revisionTime"
:
"2017-01-08T23:12:12Z"
},
{
"checksumSHA1"
:
"
vopCLXHzYm+3l5fPKOf4/fQwrCM
="
,
"checksumSHA1"
:
"
0LL9u9tfv1KPBjNEiMDP6q7lpog
="
,
"path"
:
"github.com/prometheus/common/model"
,
"revision"
:
"
3007b6072c17c8d985734e6e19b1dea9174e13d3
"
,
"revisionTime"
:
"2017-02-
19T00:35:58+01:00
"
"revision"
:
"
49fee292b27bfff7f354ee0f64e1bc4850462edf
"
,
"revisionTime"
:
"2017-02-
20T10:38:46Z
"
},
{
"checksumSHA1"
:
"ZbbESWBHHcPUJ/A5yrzKhTHuPc8="
,
...
...
@@ -690,12 +676,6 @@
"revision"
:
"c589d0c9f0d81640c518354c7bcae77d99820aa3"
,
"revisionTime"
:
"2016-09-30T00:14:02Z"
},
{
"checksumSHA1"
:
"sFD8LpJPQtWLwGda3edjf5mNUbs="
,
"path"
:
"github.com/vaughan0/go-ini"
,
"revision"
:
"a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
,
"revisionTime"
:
"2013-09-23T16:52:12+02:00"
},
{
"checksumSHA1"
:
"9jjO5GjLa0XF/nfWihF02RoH4qc="
,
"path"
:
"golang.org/x/net/context"
,
...
...
@@ -729,12 +709,6 @@
"revision"
:
"c589d0c9f0d81640c518354c7bcae77d99820aa3"
,
"revisionTime"
:
"2016-09-30T00:14:02Z"
},
{
"checksumSHA1"
:
"/k7k6eJDkxXx6K9Zpo/OwNm58XM="
,
"path"
:
"golang.org/x/net/internal/timeseries"
,
"revision"
:
"6250b412798208e6c90b03b7c4f226de5aa299e2"
,
"revisionTime"
:
"2016-08-24T22:20:41Z"
},
{
"checksumSHA1"
:
"yhndhWXMs/VSEDLks4dNyFMQStA="
,
"origin"
:
"k8s.io/client-go/1.5/vendor/golang.org/x/net/lex/httplex"
,
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录