mirror of https://github.com/knative/client.git
upgrade to latest dependencies (#1164)
Signed-off-by: Knative Automation <automation@knative.team>
This commit is contained in:
parent
24945049e7
commit
03d8236b62
8
go.mod
8
go.mod
|
|
@ -22,11 +22,11 @@ require (
|
||||||
k8s.io/cli-runtime v0.18.8
|
k8s.io/cli-runtime v0.18.8
|
||||||
k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible
|
k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible
|
||||||
k8s.io/code-generator v0.18.12
|
k8s.io/code-generator v0.18.12
|
||||||
knative.dev/eventing v0.19.1-0.20201201072837-da18ee0a75f9
|
knative.dev/eventing v0.19.1-0.20201202221809-1d3519c16565
|
||||||
knative.dev/hack v0.0.0-20201201234937-fddbf732e450
|
knative.dev/hack v0.0.0-20201201234937-fddbf732e450
|
||||||
knative.dev/networking v0.0.0-20201125133435-4b21f11ccfa7
|
knative.dev/networking v0.0.0-20201203234509-4cd0793eed11
|
||||||
knative.dev/pkg v0.0.0-20201130192436-e5346d90e980
|
knative.dev/pkg v0.0.0-20201204013209-b89ac2a63293
|
||||||
knative.dev/serving v0.19.1-0.20201201235537-c8e2ccb33125
|
knative.dev/serving v0.19.1-0.20201204004409-4bbc460995d6
|
||||||
sigs.k8s.io/yaml v1.2.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
25
go.sum
25
go.sum
|
|
@ -123,6 +123,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
|
||||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||||
|
github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
|
||||||
|
github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
|
||||||
github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=
|
github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=
|
||||||
github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||||
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||||
|
|
@ -1221,29 +1223,30 @@ k8s.io/legacy-cloud-providers v0.18.8/go.mod h1:tgp4xYf6lvjrWnjQwTOPvWQE9IVqSBGP
|
||||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||||
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE=
|
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE=
|
||||||
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
knative.dev/caching v0.0.0-20201125150135-91fb5833c3c6/go.mod h1:IocU4F1XK8udXpTRC4m9KWyEVtT928sSP+T7Oy34UDg=
|
knative.dev/caching v0.0.0-20201202014037-fc10335afb00/go.mod h1:LWpD8X3fJcsDzfika4rzu1avRJqCmv2OOyTNoD8OHsQ=
|
||||||
knative.dev/eventing v0.19.1-0.20201201072837-da18ee0a75f9 h1:BdM5UGM+zs5NuVAtBqnZ4WeOmxbfqen4cYzNGsT2gsE=
|
knative.dev/eventing v0.19.1-0.20201202221809-1d3519c16565 h1:Xj/WF2mG4tyJvGEsuh6V311J/nwP3wPRJ1ioMO/Urs0=
|
||||||
knative.dev/eventing v0.19.1-0.20201201072837-da18ee0a75f9/go.mod h1:Kd4J2Lcc6L0LMIVOUKYR5utJtTYbX201qZGgDDEWGrQ=
|
knative.dev/eventing v0.19.1-0.20201202221809-1d3519c16565/go.mod h1:LeCXV+DSK6WMrgTJVE/Vyrpj5mo1BzbnpucjQh8zYf4=
|
||||||
knative.dev/hack v0.0.0-20201112185459-01a34c573bd8 h1:RNbZsAjhswBPtl4C5C5gEFX5/GfWIOZQxfYD9DhkHdY=
|
knative.dev/hack v0.0.0-20201112185459-01a34c573bd8 h1:RNbZsAjhswBPtl4C5C5gEFX5/GfWIOZQxfYD9DhkHdY=
|
||||||
knative.dev/hack v0.0.0-20201112185459-01a34c573bd8/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
|
knative.dev/hack v0.0.0-20201112185459-01a34c573bd8/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
|
||||||
knative.dev/hack v0.0.0-20201118155651-b31d3bb6bff9/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
|
knative.dev/hack v0.0.0-20201118155651-b31d3bb6bff9/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
|
||||||
knative.dev/hack v0.0.0-20201120192952-353db687ec5b h1:Lc+AKgwhAZUD98mN++qTHeeaP6FRmS8fcwc/rXkP8G0=
|
knative.dev/hack v0.0.0-20201120192952-353db687ec5b h1:Lc+AKgwhAZUD98mN++qTHeeaP6FRmS8fcwc/rXkP8G0=
|
||||||
knative.dev/hack v0.0.0-20201120192952-353db687ec5b/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
|
knative.dev/hack v0.0.0-20201120192952-353db687ec5b/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
|
||||||
knative.dev/hack v0.0.0-20201125230335-c46a6498e9ed h1:DrTU+vxQrNJtySxyNovUtl4si0ozjtU/AYhYaTz34YA=
|
|
||||||
knative.dev/hack v0.0.0-20201125230335-c46a6498e9ed/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
|
|
||||||
knative.dev/hack v0.0.0-20201201234937-fddbf732e450 h1:IyitWF7OzfunCgE4b9ZsJAeIRoQ6JOyqDrz/19X4iS4=
|
knative.dev/hack v0.0.0-20201201234937-fddbf732e450 h1:IyitWF7OzfunCgE4b9ZsJAeIRoQ6JOyqDrz/19X4iS4=
|
||||||
knative.dev/hack v0.0.0-20201201234937-fddbf732e450/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
|
knative.dev/hack v0.0.0-20201201234937-fddbf732e450/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
|
||||||
knative.dev/networking v0.0.0-20201125133435-4b21f11ccfa7 h1:O40i4ZCYfpaw7m1yUwtmRhBOdT0HRp3MYD4PWaRzYqI=
|
knative.dev/networking v0.0.0-20201203005409-47ea2396c447/go.mod h1:9MYaP1/cs1zYvvvr+gda2XRW1e1WqmUE0xKVxkwmUXw=
|
||||||
knative.dev/networking v0.0.0-20201125133435-4b21f11ccfa7/go.mod h1:EpImbpW3gu85Mg39GT7gUJTVYLtFYWDsZQCZlKFmGUM=
|
knative.dev/networking v0.0.0-20201203234509-4cd0793eed11 h1:sogSEftyYRTNZ3g2Ynmvw8djj9X0RwBNa9GCDDLQMr4=
|
||||||
|
knative.dev/networking v0.0.0-20201203234509-4cd0793eed11/go.mod h1:vj8h8ODz+HnStJgIgvAY2pyc146SJA1CaB9iKFpSjiM=
|
||||||
knative.dev/pkg v0.0.0-20201117221452-0fccc54273ed h1:4LNEFhvD2Ya4WgCD3SC1WGXz81bjEvQgbcpe5XACI3I=
|
knative.dev/pkg v0.0.0-20201117221452-0fccc54273ed h1:4LNEFhvD2Ya4WgCD3SC1WGXz81bjEvQgbcpe5XACI3I=
|
||||||
knative.dev/pkg v0.0.0-20201117221452-0fccc54273ed/go.mod h1:nxlh3CUvx6WBPr1WKD96AHxFZPD2UKRDo9RUp8ILTyQ=
|
knative.dev/pkg v0.0.0-20201117221452-0fccc54273ed/go.mod h1:nxlh3CUvx6WBPr1WKD96AHxFZPD2UKRDo9RUp8ILTyQ=
|
||||||
knative.dev/pkg v0.0.0-20201125095035-9bf616d2f46a h1:pdJpLaq50mLKrPWYSQgTH2p64Dk7Fq/xID6l0F69cVY=
|
|
||||||
knative.dev/pkg v0.0.0-20201125095035-9bf616d2f46a/go.mod h1:wXZqP8MXCxb51yNFlecA13BwG7Hk370SWDFWV4dx4ug=
|
|
||||||
knative.dev/pkg v0.0.0-20201130192436-e5346d90e980 h1:44zakwrFjbivCM/YR+ni70e9u1ELtwA7VF1HSmUcj+g=
|
knative.dev/pkg v0.0.0-20201130192436-e5346d90e980 h1:44zakwrFjbivCM/YR+ni70e9u1ELtwA7VF1HSmUcj+g=
|
||||||
knative.dev/pkg v0.0.0-20201130192436-e5346d90e980/go.mod h1:wXZqP8MXCxb51yNFlecA13BwG7Hk370SWDFWV4dx4ug=
|
knative.dev/pkg v0.0.0-20201130192436-e5346d90e980/go.mod h1:wXZqP8MXCxb51yNFlecA13BwG7Hk370SWDFWV4dx4ug=
|
||||||
|
knative.dev/pkg v0.0.0-20201203005309-e45bbefd1d63/go.mod h1:efRLl6Z597X5r3TTt/I73cQIDDV1He6LMN1y4HQlkHU=
|
||||||
|
knative.dev/pkg v0.0.0-20201203193109-a536174bdf99/go.mod h1:Jtbz2/1gB8EZgZA4VvabTxM+AcDt2WL2P0RaFnKcOX8=
|
||||||
|
knative.dev/pkg v0.0.0-20201204013209-b89ac2a63293 h1:6iXRbYUw05l6vnKiUsZSE1HRbsc8hulSs9Y2mTGVMxM=
|
||||||
|
knative.dev/pkg v0.0.0-20201204013209-b89ac2a63293/go.mod h1:Jtbz2/1gB8EZgZA4VvabTxM+AcDt2WL2P0RaFnKcOX8=
|
||||||
knative.dev/reconciler-test v0.0.0-20201124190335-83a44efcdfef/go.mod h1:YSs1y1rgnjs8w39/drLIOQbWvZUQwqApvd+EizO8UsA=
|
knative.dev/reconciler-test v0.0.0-20201124190335-83a44efcdfef/go.mod h1:YSs1y1rgnjs8w39/drLIOQbWvZUQwqApvd+EizO8UsA=
|
||||||
knative.dev/serving v0.19.1-0.20201201235537-c8e2ccb33125 h1:VHSN0ro9go0wOBJ/oENy2UTEDKDBP6Yylco9bD1ZWok=
|
knative.dev/serving v0.19.1-0.20201204004409-4bbc460995d6 h1:8oq1P5XKceDVdBk9sqIc6IvdRP7xV00CRKMWTo/kAzU=
|
||||||
knative.dev/serving v0.19.1-0.20201201235537-c8e2ccb33125/go.mod h1:AugxGYLC/dnisz5X6huxxMbi2YJswwuzlDp2LEu293c=
|
knative.dev/serving v0.19.1-0.20201204004409-4bbc460995d6/go.mod h1:0V/DzxYUwB2dCUbn9V0BP2csI5r2ULLxQAGGNr6ZxB0=
|
||||||
pgregory.net/rapid v0.3.3/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
|
pgregory.net/rapid v0.3.3/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) Blendle
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
vendor/
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) Blendle
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
@ -0,0 +1,347 @@
|
||||||
|
# :zap: Zapdriver
|
||||||
|
|
||||||
|
Blazing fast, [Zap][zap]-based [Stackdriver][stackdriver] logging.
|
||||||
|
|
||||||
|
[zap]: https://github.com/uber-go/zap
|
||||||
|
[stackdriver]: https://cloud.google.com/stackdriver/
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
This package provides three building blocks to support the full array of
|
||||||
|
structured logging capabilities of Stackdriver:
|
||||||
|
|
||||||
|
* [Special purpose logging fields](#special-purpose-logging-fields)
|
||||||
|
* [Pre-configured Stackdriver-optimized encoder](#pre-configured-stackdriver-optimized-encoder)
|
||||||
|
* [Custom Stackdriver Zap core](#custom-stackdriver-zap-core)
|
||||||
|
* [Using Error Reporting](#using-error-reporting)
|
||||||
|
|
||||||
|
The above components can be used separately, but to start, you can create a new
|
||||||
|
Zap logger with all of the above included:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
logger, err := zapdriver.NewProduction() // with sampling
|
||||||
|
logger, err := zapdriver.NewDevelopment() // with `development` set to `true`
|
||||||
|
```
|
||||||
|
|
||||||
|
The above functions give back a pointer to a `zap.Logger` object, so you can use
|
||||||
|
[Zap][zap] like you've always done, except that it now logs in the proper
|
||||||
|
[Stackdriver][stackdriver] format.
|
||||||
|
|
||||||
|
You can also create a configuration struct, and build your logger from there:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
config := zapdriver.NewProductionConfig()
|
||||||
|
config := zapdriver.NewDevelopmentConfig()
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, get the Zapdriver encoder, and build your own configuration struct from
|
||||||
|
that:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
encoder := zapdriver.NewProductionEncoderConfig()
|
||||||
|
encoder := zapdriver.NewDevelopmentEncoderConfig()
|
||||||
|
```
|
||||||
|
|
||||||
|
Read on to learn more about the available Stackdriver-specific log fields, and
|
||||||
|
how to use the above-mentioned components.
|
||||||
|
|
||||||
|
### Special purpose logging fields
|
||||||
|
|
||||||
|
You can use the following fields to add extra information to your log entries.
|
||||||
|
These fields are parsed by Stackdriver to make it easier to query your logs or
|
||||||
|
to use the log details in the Stackdriver monitoring interface.
|
||||||
|
|
||||||
|
* [`HTTP`](#http)
|
||||||
|
* [`Label`](#label)
|
||||||
|
* [`SourceLocation`](#sourcelocation)
|
||||||
|
* [`Operation`](#operation)
|
||||||
|
* [`TraceContext`](#tracecontext)
|
||||||
|
|
||||||
|
#### HTTP
|
||||||
|
|
||||||
|
You can log HTTP request/response cycles using the following field:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
HTTP(req *HTTPPayload) zap.Field
|
||||||
|
```
|
||||||
|
|
||||||
|
You can either manually build the request payload:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
req := &HTTPPayload{
|
||||||
|
RequestMethod: "GET",
|
||||||
|
RequestURL: "/",
|
||||||
|
Status: 200,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, you can auto generate the struct, based on the available request and
|
||||||
|
response objects:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
NewHTTP(req *http.Request, res *http.Response) *HTTPPayload
|
||||||
|
```
|
||||||
|
|
||||||
|
You are free to pass in `nil` for either the request or response object, if one
|
||||||
|
of them is unavailable to you at the point of logging. Any field depending on
|
||||||
|
one or the other will be omitted if `nil` is passed in.
|
||||||
|
|
||||||
|
Note that there are some fields that are not populated by either the request or
|
||||||
|
response object, and need to be set manually:
|
||||||
|
|
||||||
|
* `ServerIP string`
|
||||||
|
* `Latency string`
|
||||||
|
* `CacheLookup bool`
|
||||||
|
* `CacheHit bool`
|
||||||
|
* `CacheValidatedWithOriginServer bool`
|
||||||
|
* `CacheFillBytes string`
|
||||||
|
|
||||||
|
If you have no need for those fields, the quickest way to get started is like
|
||||||
|
so:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
logger.Info("Request Received.", zapdriver.HTTP(zapdriver.NewHTTP(req, res)))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Label
|
||||||
|
|
||||||
|
You can add a "label" to your payload as follows:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
Label(key, value string) zap.Field
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that underwater, this sets the key to `labels.<key>`. You need to be using
|
||||||
|
the `zapdriver.Core` core for this to be converted to the proper format for
|
||||||
|
Stackdriver to recognize the labels.
|
||||||
|
|
||||||
|
See "Custom Stackdriver Zap core" for more details.
|
||||||
|
|
||||||
|
If you have a reason not to use the provided Core, you can still wrap labels in
|
||||||
|
the right `labels` namespace by using the available function:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
Labels(fields ...zap.Field) zap.Field
|
||||||
|
```
|
||||||
|
|
||||||
|
Like so:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
logger.Info(
|
||||||
|
"Did something.",
|
||||||
|
zapdriver.Labels(
|
||||||
|
zapdriver.Label("hello", "world"),
|
||||||
|
zapdriver.Label("hi", "universe"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Again, wrapping the `Label` calls in `Labels` is not required if you use the
|
||||||
|
supplied Zap Core.
|
||||||
|
|
||||||
|
#### SourceLocation
|
||||||
|
|
||||||
|
You can add a source code location to your log lines to be picked up by
|
||||||
|
Stackdriver.
|
||||||
|
|
||||||
|
Note that you can set this manually, or use `zapdriver.Core` to automatically
|
||||||
|
add this. If you set it manually, _and_ use `zapdriver.Core`, the manual call
|
||||||
|
stack will be preserved over the automated one.
|
||||||
|
|
||||||
|
```golang
|
||||||
|
SourceLocation(pc uintptr, file string, line int, ok bool) zap.Field
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the function signature equals that of the return values of
|
||||||
|
`runtime.Caller()`. This allows you to catch the stack frame at one location,
|
||||||
|
while logging it at a different location, like so:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
pc, file, line, ok := runtime.Caller(0)
|
||||||
|
|
||||||
|
// do other stuff...
|
||||||
|
|
||||||
|
logger.Error("Something happened!", zapdriver.SourceLocation(pc, file, line, ok))
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use `zapdriver.Core`, the above use-case is the only use-case where you
|
||||||
|
would want to manually set the source location. In all other situations, you can
|
||||||
|
simply omit this field, and it will be added automatically, using the stack
|
||||||
|
frame at the location where the log line is triggered.
|
||||||
|
|
||||||
|
If you don't use `zapdriver.Core`, and still want to add the source location at
|
||||||
|
the frame of the triggered log line, you'd do it like this:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
logger.Error("Something happened!", zapdriver.SourceLocation(runtime.Caller(0)))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Operation
|
||||||
|
|
||||||
|
The `Operation` log field allows you to group log lines into a single
|
||||||
|
"operation" performed by the application:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
Operation(id, producer string, first, last bool) zap.Field
|
||||||
|
```
|
||||||
|
|
||||||
|
For a pair of logs that belong to the same operation, you should use the same
|
||||||
|
`id` between them. The `producer` is an arbitrary identifier that should be
|
||||||
|
globally unique amongst all the logs of all your applications (meaning it should
|
||||||
|
probably be the unique name of the current application). You should set `first`
|
||||||
|
to true for the first log in the operation, and `last` to true for the final log
|
||||||
|
of the operation.
|
||||||
|
|
||||||
|
```golang
|
||||||
|
logger.Info("Started.", zapdriver.Operation("3g4d3g", "my-app", true, false))
|
||||||
|
logger.Debug("Progressing.", zapdriver.Operation("3g4d3g", "my-app", false, false))
|
||||||
|
logger.Info("Done.", zapdriver.Operation("3g4d3g", "my-app", false, true))
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead of defining the "start" and "end" booleans, you can also use these three
|
||||||
|
convenience functions:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
OperationStart(id, producer string) zap.Field
|
||||||
|
OperationCont(id, producer string) zap.Field
|
||||||
|
OperationEnd(id, producer string) zap.Field
|
||||||
|
```
|
||||||
|
|
||||||
|
#### TraceContext
|
||||||
|
|
||||||
|
You can add trace context information to your log lines to be picked up by
|
||||||
|
Stackdriver.
|
||||||
|
|
||||||
|
```golang
|
||||||
|
TraceContext(trace string, spanId string, sampled bool, projectName string) []zap.Field
|
||||||
|
```
|
||||||
|
|
||||||
|
Like so:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
logger.Error("Something happened!", zapdriver.TraceContext("105445aa7843bc8bf206b120001000", "0", true, "my-project-name")...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pre-configured Stackdriver-optimized encoder
|
||||||
|
|
||||||
|
The Stackdriver encoder maps all Zap log levels to the appropriate
|
||||||
|
[Stackdriver-supported levels][levels]:
|
||||||
|
|
||||||
|
> DEBUG (100) Debug or trace information.
|
||||||
|
>
|
||||||
|
> INFO (200) Routine information, such as ongoing status or performance.
|
||||||
|
>
|
||||||
|
> WARNING (400) Warning events might cause problems.
|
||||||
|
>
|
||||||
|
> ERROR (500) Error events are likely to cause problems.
|
||||||
|
>
|
||||||
|
> CRITICAL (600) Critical events cause more severe problems or outages.
|
||||||
|
>
|
||||||
|
> ALERT (700) A person must take an action immediately.
|
||||||
|
>
|
||||||
|
> EMERGENCY (800) One or more systems are unusable.
|
||||||
|
|
||||||
|
[levels]: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
|
||||||
|
|
||||||
|
It also sets some of the default keys to use [the right names][names], such as
|
||||||
|
`timestamp`, `severity`, and `message`.
|
||||||
|
|
||||||
|
[names]: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
|
||||||
|
|
||||||
|
You can use this encoder if you want to build your Zap logger configuration
|
||||||
|
manually:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
zapdriver.NewProductionEncoderConfig()
|
||||||
|
```
|
||||||
|
|
||||||
|
For parity-sake, there's also `zapdriver.NewDevelopmentEncoderConfig()`, but it
|
||||||
|
returns the exact same encoder right now.
|
||||||
|
|
||||||
|
### Custom Stackdriver Zap core
|
||||||
|
|
||||||
|
A custom Zap core is included in this package to support some special use-cases.
|
||||||
|
|
||||||
|
First of all, if you use `zapdriver.NewProduction()` (or `NewDevelopment`) , you
|
||||||
|
already have this core enabled, so everything _just works_ ™.
|
||||||
|
|
||||||
|
There are two use-cases which require this core:
|
||||||
|
|
||||||
|
1. If you use `zapdriver.Label("hello", "world")`, it will initially end up in
|
||||||
|
your log with the key `labels.hello` and value `world`. Now if you have two
|
||||||
|
labels, you could also have `labels.hi` with value `universe`. This works as-
|
||||||
|
is, but for this to be correctly parsed by Stackdriver as true "labels", you
|
||||||
|
need to use the Zapdriver core, so that both of these fields get rewritten,
|
||||||
|
to use the namespace `labels`, and use the keys `hello` and `hi` within that
|
||||||
|
namespace. This is done automatically.
|
||||||
|
|
||||||
|
2. If you don't want to use `zapdriver.SourceLocation()` on every log call, you
|
||||||
|
can use this core for the source location to be automatically added to
|
||||||
|
each log entry.
|
||||||
|
|
||||||
|
When building a logger, you can inject the Zapdriver core as follows:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
config := &zap.Config{}
|
||||||
|
logger, err := config.Build(zapdriver.WrapCore())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Error Reporting
|
||||||
|
|
||||||
|
To report errors using StackDriver's Error Reporting tool, a log line needs to follow a separate log format described in the [Error Reporting][errorreporting] documentation.
|
||||||
|
|
||||||
|
[errorreporting]: https://cloud.google.com/error-reporting/docs/formatting-error-messages
|
||||||
|
|
||||||
|
The simplest way to do this is by using `NewProductionWithCore`:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
logger, err := zapdriver.NewProductionWithCore(zapdriver.WrapCore(
|
||||||
|
zapdriver.ReportAllErrors(true),
|
||||||
|
zapdriver.ServiceName("my service"),
|
||||||
|
))
|
||||||
|
```
|
||||||
|
|
||||||
|
For parity-sake, there's also `zapdriver.NewDevelopmentWithCore()`
|
||||||
|
|
||||||
|
If you are building a custom logger, you can use `WrapCore()` to configure the driver core:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
config := &zap.Config{}
|
||||||
|
logger, err := config.Build(zapdriver.WrapCore(
|
||||||
|
zapdriver.ReportAllErrors(true),
|
||||||
|
zapdriver.ServiceName("my service"),
|
||||||
|
))
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuring this way, every error log entry will be reported to Stackdriver's Error Reporting tool.
|
||||||
|
|
||||||
|
#### Reporting errors manually
|
||||||
|
|
||||||
|
If you do not want every error to be reported, you can attach `ErrorReport()` to log call manually:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
logger.Error("An error to be reported!", zapdriver.ErrorReport(runtime.Caller(0)))
|
||||||
|
// Or get Caller details
|
||||||
|
pc, file, line, ok := runtime.Caller(0)
|
||||||
|
// do other stuff... and log elsewhere
|
||||||
|
logger.Error("Another error to be reported!", zapdriver.ErrorReport(pc, file, line, ok))
|
||||||
|
```
|
||||||
|
|
||||||
|
Please keep in mind that ErrorReport needs a ServiceContext attached to the log
|
||||||
|
entry. If you did not configure this using `WrapCore`, error reports will
|
||||||
|
get attached using service name as `unknown`. To prevent this from happeneing,
|
||||||
|
either configure your core or attach service context before (or when) using
|
||||||
|
the logger:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
logger.Error(
|
||||||
|
"An error to be reported!",
|
||||||
|
zapdriver.ErrorReport(runtime.Caller(0)),
|
||||||
|
zapdriver.ServiceContext("my service"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Or permanently attach it to your logger
|
||||||
|
logger = logger.With(zapdriver.ServiceContext("my service"))
|
||||||
|
// and then use it
|
||||||
|
logger.Error("An error to be reported!", zapdriver.ErrorReport(runtime.Caller(0)))
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewProductionEncoderConfig returns an opinionated EncoderConfig for
|
||||||
|
// production environments.
|
||||||
|
func NewProductionEncoderConfig() zapcore.EncoderConfig {
|
||||||
|
return encoderConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
|
||||||
|
// development environments.
|
||||||
|
func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
|
||||||
|
return encoderConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProductionConfig is a reasonable production logging configuration.
|
||||||
|
// Logging is enabled at InfoLevel and above.
|
||||||
|
//
|
||||||
|
// It uses a JSON encoder, writes to standard error, and enables sampling.
|
||||||
|
// Stacktraces are automatically included on logs of ErrorLevel and above.
|
||||||
|
func NewProductionConfig() zap.Config {
|
||||||
|
return zap.Config{
|
||||||
|
Level: zap.NewAtomicLevelAt(zap.InfoLevel),
|
||||||
|
Development: false,
|
||||||
|
Sampling: &zap.SamplingConfig{
|
||||||
|
Initial: 100,
|
||||||
|
Thereafter: 100,
|
||||||
|
},
|
||||||
|
Encoding: "json",
|
||||||
|
EncoderConfig: NewProductionEncoderConfig(),
|
||||||
|
OutputPaths: []string{"stderr"},
|
||||||
|
ErrorOutputPaths: []string{"stderr"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDevelopmentConfig is a reasonable development logging configuration.
|
||||||
|
// Logging is enabled at DebugLevel and above.
|
||||||
|
//
|
||||||
|
// It enables development mode (which makes DPanicLevel logs panic), uses a
|
||||||
|
// console encoder, writes to standard error, and disables sampling.
|
||||||
|
// Stacktraces are automatically included on logs of WarnLevel and above.
|
||||||
|
func NewDevelopmentConfig() zap.Config {
|
||||||
|
return zap.Config{
|
||||||
|
Level: zap.NewAtomicLevelAt(zap.DebugLevel),
|
||||||
|
Development: true,
|
||||||
|
Encoding: "json",
|
||||||
|
EncoderConfig: NewDevelopmentEncoderConfig(),
|
||||||
|
OutputPaths: []string{"stderr"},
|
||||||
|
ErrorOutputPaths: []string{"stderr"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,243 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// driverConfig is used to configure core.
|
||||||
|
type driverConfig struct {
|
||||||
|
// Report all logs with level error or above to stackdriver using
|
||||||
|
// `ErrorReport()` when set to true
|
||||||
|
ReportAllErrors bool
|
||||||
|
|
||||||
|
// ServiceName is added as `ServiceContext()` to all logs when set
|
||||||
|
ServiceName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Core is a zapdriver specific core wrapped around the default zap core. It
|
||||||
|
// allows to merge all defined labels
|
||||||
|
type core struct {
|
||||||
|
zapcore.Core
|
||||||
|
|
||||||
|
// permLabels is a collection of labels that have been added to the logger
|
||||||
|
// through the use of `With()`. These labels should never be cleared after
|
||||||
|
// logging a single entry, unlike `tempLabel`.
|
||||||
|
permLabels *labels
|
||||||
|
|
||||||
|
// tempLabels keeps a record of all the labels that need to be applied to the
|
||||||
|
// current log entry. Zap serializes log fields at different parts of the
|
||||||
|
// stack, one such location is when calling `core.With` and the other one is
|
||||||
|
// when calling `core.Write`. This makes it impossible to (for example) take
|
||||||
|
// all `labels.xxx` fields, and wrap them in the `labels` namespace in one go.
|
||||||
|
//
|
||||||
|
// Instead, we have to filter out these labels at both locations, and then add
|
||||||
|
// them back in the proper format right before we call `Write` on the original
|
||||||
|
// Zap core.
|
||||||
|
tempLabels *labels
|
||||||
|
|
||||||
|
// Configuration for the zapdriver core
|
||||||
|
config driverConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// zapdriver core option to report all logs with level error or above to stackdriver
|
||||||
|
// using `ErrorReport()` when set to true
|
||||||
|
func ReportAllErrors(report bool) func(*core) {
|
||||||
|
return func(c *core) {
|
||||||
|
c.config.ReportAllErrors = report
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// zapdriver core option to add `ServiceContext()` to all logs with `name` as
|
||||||
|
// service name
|
||||||
|
func ServiceName(name string) func(*core) {
|
||||||
|
return func(c *core) {
|
||||||
|
c.config.ServiceName = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapCore returns a `zap.Option` that wraps the default core with the
|
||||||
|
// zapdriver one.
|
||||||
|
func WrapCore(options ...func(*core)) zap.Option {
|
||||||
|
return zap.WrapCore(func(c zapcore.Core) zapcore.Core {
|
||||||
|
newcore := &core{
|
||||||
|
Core: c,
|
||||||
|
permLabels: newLabels(),
|
||||||
|
tempLabels: newLabels(),
|
||||||
|
}
|
||||||
|
for _, option := range options {
|
||||||
|
option(newcore)
|
||||||
|
}
|
||||||
|
return newcore
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// With adds structured context to the Core.
|
||||||
|
func (c *core) With(fields []zap.Field) zapcore.Core {
|
||||||
|
var lbls *labels
|
||||||
|
lbls, fields = c.extractLabels(fields)
|
||||||
|
|
||||||
|
lbls.mutex.RLock()
|
||||||
|
c.permLabels.mutex.Lock()
|
||||||
|
for k, v := range lbls.store {
|
||||||
|
c.permLabels.store[k] = v
|
||||||
|
}
|
||||||
|
c.permLabels.mutex.Unlock()
|
||||||
|
lbls.mutex.RUnlock()
|
||||||
|
|
||||||
|
return &core{
|
||||||
|
Core: c.Core.With(fields),
|
||||||
|
permLabels: c.permLabels,
|
||||||
|
tempLabels: newLabels(),
|
||||||
|
config: c.config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check determines whether the supplied Entry should be logged (using the
|
||||||
|
// embedded LevelEnabler and possibly some extra logic). If the entry
|
||||||
|
// should be logged, the Core adds itself to the CheckedEntry and returns
|
||||||
|
// the result.
|
||||||
|
//
|
||||||
|
// Callers must use Check before calling Write.
|
||||||
|
func (c *core) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
||||||
|
if c.Enabled(ent.Level) {
|
||||||
|
return ce.AddCore(ent, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ce
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *core) Write(ent zapcore.Entry, fields []zapcore.Field) error {
|
||||||
|
var lbls *labels
|
||||||
|
lbls, fields = c.extractLabels(fields)
|
||||||
|
|
||||||
|
lbls.mutex.RLock()
|
||||||
|
c.tempLabels.mutex.Lock()
|
||||||
|
for k, v := range lbls.store {
|
||||||
|
c.tempLabels.store[k] = v
|
||||||
|
}
|
||||||
|
c.tempLabels.mutex.Unlock()
|
||||||
|
lbls.mutex.RUnlock()
|
||||||
|
|
||||||
|
fields = append(fields, labelsField(c.allLabels()))
|
||||||
|
fields = c.withSourceLocation(ent, fields)
|
||||||
|
if c.config.ServiceName != "" {
|
||||||
|
fields = c.withServiceContext(c.config.ServiceName, fields)
|
||||||
|
}
|
||||||
|
if c.config.ReportAllErrors && zapcore.ErrorLevel.Enabled(ent.Level) {
|
||||||
|
fields = c.withErrorReport(ent, fields)
|
||||||
|
if c.config.ServiceName == "" {
|
||||||
|
// A service name was not set but error report needs it
|
||||||
|
// So attempt to add a generic service name
|
||||||
|
fields = c.withServiceContext("unknown", fields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.tempLabels.reset()
|
||||||
|
|
||||||
|
return c.Core.Write(ent, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync flushes buffered logs (if any).
|
||||||
|
func (c *core) Sync() error {
|
||||||
|
return c.Core.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *core) allLabels() *labels {
|
||||||
|
lbls := newLabels()
|
||||||
|
|
||||||
|
lbls.mutex.Lock()
|
||||||
|
c.permLabels.mutex.RLock()
|
||||||
|
for k, v := range c.permLabels.store {
|
||||||
|
lbls.store[k] = v
|
||||||
|
}
|
||||||
|
c.permLabels.mutex.RUnlock()
|
||||||
|
|
||||||
|
c.tempLabels.mutex.RLock()
|
||||||
|
for k, v := range c.tempLabels.store {
|
||||||
|
lbls.store[k] = v
|
||||||
|
}
|
||||||
|
c.tempLabels.mutex.RUnlock()
|
||||||
|
lbls.mutex.Unlock()
|
||||||
|
|
||||||
|
return lbls
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *core) extractLabels(fields []zapcore.Field) (*labels, []zapcore.Field) {
|
||||||
|
lbls := newLabels()
|
||||||
|
out := []zapcore.Field{}
|
||||||
|
|
||||||
|
lbls.mutex.Lock()
|
||||||
|
for i := range fields {
|
||||||
|
if !isLabelField(fields[i]) {
|
||||||
|
out = append(out, fields[i])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lbls.store[strings.Replace(fields[i].Key, "labels.", "", 1)] = fields[i].String
|
||||||
|
}
|
||||||
|
lbls.mutex.Unlock()
|
||||||
|
|
||||||
|
return lbls, out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *core) withLabels(fields []zapcore.Field) []zapcore.Field {
|
||||||
|
lbls := newLabels()
|
||||||
|
out := []zapcore.Field{}
|
||||||
|
|
||||||
|
lbls.mutex.Lock()
|
||||||
|
for i := range fields {
|
||||||
|
if isLabelField(fields[i]) {
|
||||||
|
lbls.store[strings.Replace(fields[i].Key, "labels.", "", 1)] = fields[i].String
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, fields[i])
|
||||||
|
}
|
||||||
|
lbls.mutex.Unlock()
|
||||||
|
|
||||||
|
return append(out, labelsField(lbls))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *core) withSourceLocation(ent zapcore.Entry, fields []zapcore.Field) []zapcore.Field {
|
||||||
|
// If the source location was manually set, don't overwrite it
|
||||||
|
for i := range fields {
|
||||||
|
if fields[i].Key == sourceKey {
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ent.Caller.Defined {
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(fields, SourceLocation(ent.Caller.PC, ent.Caller.File, ent.Caller.Line, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *core) withServiceContext(name string, fields []zapcore.Field) []zapcore.Field {
|
||||||
|
// If the service context was manually set, don't overwrite it
|
||||||
|
for i := range fields {
|
||||||
|
if fields[i].Key == serviceContextKey {
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(fields, ServiceContext(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *core) withErrorReport(ent zapcore.Entry, fields []zapcore.Field) []zapcore.Field {
|
||||||
|
// If the error report was manually set, don't overwrite it
|
||||||
|
for i := range fields {
|
||||||
|
if fields[i].Key == contextKey {
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ent.Caller.Defined {
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(fields, ErrorReport(ent.Caller.PC, ent.Caller.File, ent.Caller.Line, true))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// logLevelSeverity maps the Zap log levels to the correct level names as
|
||||||
|
// defined by Stackdriver.
|
||||||
|
//
|
||||||
|
// DEFAULT (0) The log entry has no assigned severity level.
|
||||||
|
// DEBUG (100) Debug or trace information.
|
||||||
|
// INFO (200) Routine information, such as ongoing status or performance.
|
||||||
|
// NOTICE (300) Normal but significant events, such as start up, shut down, or a configuration change.
|
||||||
|
// WARNING (400) Warning events might cause problems.
|
||||||
|
// ERROR (500) Error events are likely to cause problems.
|
||||||
|
// CRITICAL (600) Critical events cause more severe problems or outages.
|
||||||
|
// ALERT (700) A person must take an action immediately.
|
||||||
|
// EMERGENCY (800) One or more systems are unusable.
|
||||||
|
//
|
||||||
|
// See: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
|
||||||
|
var logLevelSeverity = map[zapcore.Level]string{
|
||||||
|
zapcore.DebugLevel: "DEBUG",
|
||||||
|
zapcore.InfoLevel: "INFO",
|
||||||
|
zapcore.WarnLevel: "WARNING",
|
||||||
|
zapcore.ErrorLevel: "ERROR",
|
||||||
|
zapcore.DPanicLevel: "CRITICAL",
|
||||||
|
zapcore.PanicLevel: "ALERT",
|
||||||
|
zapcore.FatalLevel: "EMERGENCY",
|
||||||
|
}
|
||||||
|
|
||||||
|
// encoderConfig is the default encoder configuration, slightly tweaked to use
|
||||||
|
// the correct fields for Stackdriver to parse them.
|
||||||
|
var encoderConfig = zapcore.EncoderConfig{
|
||||||
|
TimeKey: "timestamp",
|
||||||
|
LevelKey: "severity",
|
||||||
|
NameKey: "logger",
|
||||||
|
CallerKey: "caller",
|
||||||
|
MessageKey: "message",
|
||||||
|
StacktraceKey: "stacktrace",
|
||||||
|
LineEnding: zapcore.DefaultLineEnding,
|
||||||
|
EncodeLevel: EncodeLevel,
|
||||||
|
EncodeTime: RFC3339NanoTimeEncoder,
|
||||||
|
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||||
|
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeLevel maps the internal Zap log level to the appropriate Stackdriver
|
||||||
|
// level.
|
||||||
|
func EncodeLevel(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
|
||||||
|
enc.AppendString(logLevelSeverity[l])
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC3339NanoTimeEncoder serializes a time.Time to an RFC3339Nano-formatted
|
||||||
|
// string with nanoseconds precision.
|
||||||
|
func RFC3339NanoTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||||
|
enc.AppendString(t.Format(time.RFC3339Nano))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
module github.com/blendle/zapdriver
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pkg/errors v0.8.1 // indirect
|
||||||
|
github.com/stretchr/testify v1.3.0
|
||||||
|
go.uber.org/atomic v1.4.0 // indirect
|
||||||
|
go.uber.org/multierr v1.1.0 // indirect
|
||||||
|
go.uber.org/zap v1.10.0
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
// "Broker: Request timed out"
|
||||||
|
// https://console.cloud.google.com/logs/viewer?project=bnl-blendle&minLogLevel=
|
||||||
|
// 0&expandAll=false×tamp=2018-05-23T22:21:56.142000000Z&customFacets=&limi
|
||||||
|
// tCustomFacetWidth=true&dateRangeEnd=2018-05-23T22:21:52.545Z&interval=PT1H&re
|
||||||
|
// source=container%2Fcluster_name%2Fblendle-2%2Fnamespace_id%2Fstream-
|
||||||
|
// composition-analytic-events-
|
||||||
|
// backfill&scrollTimestamp=2018-05-23T05:29:33.000000000Z&logName=projects
|
||||||
|
// %2Fbnl-blendle%2Flogs%2Fstream-composition-analytic-events-
|
||||||
|
// pipe-1&dateRangeUnbound=backwardInTime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTP adds the correct Stackdriver "HTTP" field.
|
||||||
|
//
|
||||||
|
// see: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#HttpRequest
|
||||||
|
func HTTP(req *HTTPPayload) zap.Field {
|
||||||
|
return zap.Object("httpRequest", req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPPayload is the complete payload that can be interpreted by
|
||||||
|
// Stackdriver as a HTTP request.
|
||||||
|
type HTTPPayload struct {
|
||||||
|
// The request method. Examples: "GET", "HEAD", "PUT", "POST".
|
||||||
|
RequestMethod string `json:"requestMethod"`
|
||||||
|
|
||||||
|
// The scheme (http, https), the host name, the path and the query portion of
|
||||||
|
// the URL that was requested.
|
||||||
|
//
|
||||||
|
// Example: "http://example.com/some/info?color=red".
|
||||||
|
RequestURL string `json:"requestUrl"`
|
||||||
|
|
||||||
|
// The size of the HTTP request message in bytes, including the request
|
||||||
|
// headers and the request body.
|
||||||
|
RequestSize string `json:"requestSize"`
|
||||||
|
|
||||||
|
// The response code indicating the status of response.
|
||||||
|
//
|
||||||
|
// Examples: 200, 404.
|
||||||
|
Status int `json:"status"`
|
||||||
|
|
||||||
|
// The size of the HTTP response message sent back to the client, in bytes,
|
||||||
|
// including the response headers and the response body.
|
||||||
|
ResponseSize string `json:"responseSize"`
|
||||||
|
|
||||||
|
// The user agent sent by the client.
|
||||||
|
//
|
||||||
|
// Example: "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; Q312461; .NET CLR 1.0.3705)".
|
||||||
|
UserAgent string `json:"userAgent"`
|
||||||
|
|
||||||
|
// The IP address (IPv4 or IPv6) of the client that issued the HTTP request.
|
||||||
|
//
|
||||||
|
// Examples: "192.168.1.1", "FE80::0202:B3FF:FE1E:8329".
|
||||||
|
RemoteIP string `json:"remoteIp"`
|
||||||
|
|
||||||
|
// The IP address (IPv4 or IPv6) of the origin server that the request was
|
||||||
|
// sent to.
|
||||||
|
ServerIP string `json:"serverIp"`
|
||||||
|
|
||||||
|
// The referrer URL of the request, as defined in HTTP/1.1 Header Field
|
||||||
|
// Definitions.
|
||||||
|
Referer string `json:"referer"`
|
||||||
|
|
||||||
|
// The request processing latency on the server, from the time the request was
|
||||||
|
// received until the response was sent.
|
||||||
|
//
|
||||||
|
// A duration in seconds with up to nine fractional digits, terminated by 's'.
|
||||||
|
//
|
||||||
|
// Example: "3.5s".
|
||||||
|
Latency string `json:"latency"`
|
||||||
|
|
||||||
|
// Whether or not a cache lookup was attempted.
|
||||||
|
CacheLookup bool `json:"cacheLookup"`
|
||||||
|
|
||||||
|
// Whether or not an entity was served from cache (with or without
|
||||||
|
// validation).
|
||||||
|
CacheHit bool `json:"cacheHit"`
|
||||||
|
|
||||||
|
// Whether or not the response was validated with the origin server before
|
||||||
|
// being served from cache. This field is only meaningful if cacheHit is True.
|
||||||
|
CacheValidatedWithOriginServer bool `json:"cacheValidatedWithOriginServer"`
|
||||||
|
|
||||||
|
// The number of HTTP response bytes inserted into cache. Set only when a
|
||||||
|
// cache fill was attempted.
|
||||||
|
CacheFillBytes string `json:"cacheFillBytes"`
|
||||||
|
|
||||||
|
// Protocol used for the request.
|
||||||
|
//
|
||||||
|
// Examples: "HTTP/1.1", "HTTP/2", "websocket"
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTP returns a new HTTPPayload struct, based on the passed
|
||||||
|
// in http.Request and http.Response objects.
|
||||||
|
func NewHTTP(req *http.Request, res *http.Response) *HTTPPayload {
|
||||||
|
if req == nil {
|
||||||
|
req = &http.Request{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if res == nil {
|
||||||
|
res = &http.Response{}
|
||||||
|
}
|
||||||
|
|
||||||
|
sdreq := &HTTPPayload{
|
||||||
|
RequestMethod: req.Method,
|
||||||
|
Status: res.StatusCode,
|
||||||
|
UserAgent: req.UserAgent(),
|
||||||
|
RemoteIP: req.RemoteAddr,
|
||||||
|
Referer: req.Referer(),
|
||||||
|
Protocol: req.Proto,
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.URL != nil {
|
||||||
|
sdreq.RequestURL = req.URL.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
if req.Body != nil {
|
||||||
|
n, _ := io.Copy(buf, req.Body) // nolint: gas
|
||||||
|
sdreq.RequestSize = strconv.FormatInt(n, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Body != nil {
|
||||||
|
buf.Reset()
|
||||||
|
n, _ := io.Copy(buf, res.Body) // nolint: gas
|
||||||
|
sdreq.ResponseSize = strconv.FormatInt(n, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sdreq
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalLogObject implements zapcore.ObjectMarshaller interface.
|
||||||
|
func (req HTTPPayload) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||||
|
enc.AddString("requestMethod", req.RequestMethod)
|
||||||
|
enc.AddString("requestUrl", req.RequestURL)
|
||||||
|
enc.AddString("requestSize", req.RequestSize)
|
||||||
|
enc.AddInt("status", req.Status)
|
||||||
|
enc.AddString("responseSize", req.ResponseSize)
|
||||||
|
enc.AddString("userAgent", req.UserAgent)
|
||||||
|
enc.AddString("remoteIp", req.RemoteIP)
|
||||||
|
enc.AddString("serverIp", req.ServerIP)
|
||||||
|
enc.AddString("referer", req.Referer)
|
||||||
|
enc.AddString("latency", req.Latency)
|
||||||
|
enc.AddBool("cacheLookup", req.CacheLookup)
|
||||||
|
enc.AddBool("cacheHit", req.CacheHit)
|
||||||
|
enc.AddBool("cacheValidatedWithOriginServer", req.CacheValidatedWithOriginServer)
|
||||||
|
enc.AddString("cacheFillBytes", req.CacheFillBytes)
|
||||||
|
enc.AddString("protocol", req.Protocol)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const labelsKey = "logging.googleapis.com/labels"
|
||||||
|
|
||||||
|
// Label adds an optional label to the payload.
|
||||||
|
//
|
||||||
|
// Labels are a set of user-defined (key, value) data that provides additional
|
||||||
|
// information about the log entry.
|
||||||
|
//
|
||||||
|
// Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.
|
||||||
|
func Label(key, value string) zap.Field {
|
||||||
|
return zap.String("labels."+key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Labels takes Zap fields, filters the ones that have their key start with the
|
||||||
|
// string `labels.` and their value type set to StringType. It then wraps those
|
||||||
|
// key/value pairs in a top-level `labels` namespace.
|
||||||
|
func Labels(fields ...zap.Field) zap.Field {
|
||||||
|
lbls := newLabels()
|
||||||
|
|
||||||
|
lbls.mutex.Lock()
|
||||||
|
for i := range fields {
|
||||||
|
if isLabelField(fields[i]) {
|
||||||
|
lbls.store[strings.Replace(fields[i].Key, "labels.", "", 1)] = fields[i].String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lbls.mutex.Unlock()
|
||||||
|
|
||||||
|
return labelsField(lbls)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLabelField(field zap.Field) bool {
|
||||||
|
return strings.HasPrefix(field.Key, "labels.") && field.Type == zapcore.StringType
|
||||||
|
}
|
||||||
|
|
||||||
|
func labelsField(l *labels) zap.Field {
|
||||||
|
return zap.Object(labelsKey, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
type labels struct {
|
||||||
|
store map[string]string
|
||||||
|
mutex *sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLabels() *labels {
|
||||||
|
return &labels{store: map[string]string{}, mutex: &sync.RWMutex{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labels) Add(key, value string) {
|
||||||
|
l.mutex.Lock()
|
||||||
|
l.store[key] = value
|
||||||
|
l.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labels) reset() {
|
||||||
|
l.mutex.Lock()
|
||||||
|
l.store = map[string]string{}
|
||||||
|
l.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l labels) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||||
|
l.mutex.RLock()
|
||||||
|
for k, v := range l.store {
|
||||||
|
enc.AddString(k, v)
|
||||||
|
}
|
||||||
|
l.mutex.RUnlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewProduction builds a sensible production Logger that writes InfoLevel and
|
||||||
|
// above logs to standard error as JSON.
|
||||||
|
//
|
||||||
|
// It's a shortcut for NewProductionConfig().Build(...Option).
|
||||||
|
func NewProduction(options ...zap.Option) (*zap.Logger, error) {
|
||||||
|
options = append(options, WrapCore())
|
||||||
|
|
||||||
|
return NewProductionConfig().Build(options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProductionWithCore is same as NewProduction but accepts a custom configured core
|
||||||
|
func NewProductionWithCore(core zap.Option, options ...zap.Option) (*zap.Logger, error) {
|
||||||
|
options = append(options, core)
|
||||||
|
|
||||||
|
return NewProductionConfig().Build(options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDevelopment builds a development Logger that writes DebugLevel and above
|
||||||
|
// logs to standard error in a human-friendly format.
|
||||||
|
//
|
||||||
|
// It's a shortcut for NewDevelopmentConfig().Build(...Option).
|
||||||
|
func NewDevelopment(options ...zap.Option) (*zap.Logger, error) {
|
||||||
|
options = append(options, WrapCore())
|
||||||
|
|
||||||
|
return NewDevelopmentConfig().Build(options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDevelopmentWithCore is same as NewDevelopment but accepts a custom configured core
|
||||||
|
func NewDevelopmentWithCore(core zap.Option, options ...zap.Option) (*zap.Logger, error) {
|
||||||
|
options = append(options, core)
|
||||||
|
|
||||||
|
return NewDevelopmentConfig().Build(options...)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const operationKey = "logging.googleapis.com/operation"
|
||||||
|
|
||||||
|
// Operation adds the correct Stackdriver "operation" field.
|
||||||
|
//
|
||||||
|
// Additional information about a potentially long-running operation with which
|
||||||
|
// a log entry is associated.
|
||||||
|
//
|
||||||
|
// see: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogEntryOperation
|
||||||
|
func Operation(id, producer string, first, last bool) zap.Field {
|
||||||
|
op := &operation{
|
||||||
|
ID: id,
|
||||||
|
Producer: producer,
|
||||||
|
First: first,
|
||||||
|
Last: last,
|
||||||
|
}
|
||||||
|
|
||||||
|
return zap.Object(operationKey, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperationStart is a convenience function for `Operation`. It should be called
|
||||||
|
// for the first operation log.
|
||||||
|
func OperationStart(id, producer string) zap.Field {
|
||||||
|
return Operation(id, producer, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperationCont is a convenience function for `Operation`. It should be called
|
||||||
|
// for any non-start/end operation log.
|
||||||
|
func OperationCont(id, producer string) zap.Field {
|
||||||
|
return Operation(id, producer, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperationEnd is a convenience function for `Operation`. It should be called
|
||||||
|
// for the last operation log.
|
||||||
|
func OperationEnd(id, producer string) zap.Field {
|
||||||
|
return Operation(id, producer, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// operation is the complete payload that can be interpreted by Stackdriver as
|
||||||
|
// an operation.
|
||||||
|
type operation struct {
|
||||||
|
// Optional. An arbitrary operation identifier. Log entries with the same
|
||||||
|
// identifier are assumed to be part of the same operation.
|
||||||
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// Optional. An arbitrary producer identifier. The combination of id and
|
||||||
|
// producer must be globally unique. Examples for producer:
|
||||||
|
// "MyDivision.MyBigCompany.com", "github.com/MyProject/MyApplication".
|
||||||
|
Producer string `json:"producer"`
|
||||||
|
|
||||||
|
// Optional. Set this to True if this is the first log entry in the operation.
|
||||||
|
First bool `json:"first"`
|
||||||
|
|
||||||
|
// Optional. Set this to True if this is the last log entry in the operation.
|
||||||
|
Last bool `json:"last"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalLogObject implements zapcore.ObjectMarshaller interface.
|
||||||
|
func (op operation) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||||
|
enc.AddString("id", op.ID)
|
||||||
|
enc.AddString("producer", op.Producer)
|
||||||
|
enc.AddBool("first", op.First)
|
||||||
|
enc.AddBool("last", op.Last)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const contextKey = "context"
|
||||||
|
|
||||||
|
// ErrorReport adds the correct Stackdriver "context" field for getting the log line
|
||||||
|
// reported as error.
|
||||||
|
//
|
||||||
|
// see: https://cloud.google.com/error-reporting/docs/formatting-error-messages
|
||||||
|
func ErrorReport(pc uintptr, file string, line int, ok bool) zap.Field {
|
||||||
|
return zap.Object(contextKey, newReportContext(pc, file, line, ok))
|
||||||
|
}
|
||||||
|
|
||||||
|
// reportLocation is the source code location information associated with the log entry
|
||||||
|
// for the purpose of reporting an error,
|
||||||
|
// if any.
|
||||||
|
type reportLocation struct {
|
||||||
|
File string `json:"filePath"`
|
||||||
|
Line string `json:"lineNumber"`
|
||||||
|
Function string `json:"functionName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalLogObject implements zapcore.ObjectMarshaller interface.
|
||||||
|
func (location reportLocation) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||||
|
enc.AddString("filePath", location.File)
|
||||||
|
enc.AddString("lineNumber", location.Line)
|
||||||
|
enc.AddString("functionName", location.Function)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reportContext is the context information attached to a log for reporting errors
|
||||||
|
type reportContext struct {
|
||||||
|
ReportLocation reportLocation `json:"reportLocation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalLogObject implements zapcore.ObjectMarshaller interface.
|
||||||
|
func (context reportContext) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||||
|
enc.AddObject("reportLocation", context.ReportLocation)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReportContext(pc uintptr, file string, line int, ok bool) *reportContext {
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var function string
|
||||||
|
if fn := runtime.FuncForPC(pc); fn != nil {
|
||||||
|
function = fn.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
context := &reportContext{
|
||||||
|
ReportLocation: reportLocation{
|
||||||
|
File: file,
|
||||||
|
Line: strconv.Itoa(line),
|
||||||
|
Function: function,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const serviceContextKey = "serviceContext"
|
||||||
|
|
||||||
|
// ServiceContext adds the correct service information adding the log line
|
||||||
|
// It is a required field if an error needs to be reported.
|
||||||
|
//
|
||||||
|
// see: https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext
|
||||||
|
// see: https://cloud.google.com/error-reporting/docs/formatting-error-messages
|
||||||
|
func ServiceContext(name string) zap.Field {
|
||||||
|
return zap.Object(serviceContextKey, newServiceContext(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// serviceContext describes a running service that sends errors.
|
||||||
|
// Currently it only describes a service name.
|
||||||
|
type serviceContext struct {
|
||||||
|
Name string `json:"service"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalLogObject implements zapcore.ObjectMarshaller interface.
|
||||||
|
func (service_context serviceContext) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||||
|
enc.AddString("service", service_context.Name)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newServiceContext(name string) *serviceContext {
|
||||||
|
return &serviceContext{
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const sourceKey = "logging.googleapis.com/sourceLocation"
|
||||||
|
|
||||||
|
// SourceLocation adds the correct Stackdriver "SourceLocation" field.
|
||||||
|
//
|
||||||
|
// see: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogEntrySourceLocation
|
||||||
|
func SourceLocation(pc uintptr, file string, line int, ok bool) zap.Field {
|
||||||
|
return zap.Object(sourceKey, newSource(pc, file, line, ok))
|
||||||
|
}
|
||||||
|
|
||||||
|
// source is the source code location information associated with the log entry,
|
||||||
|
// if any.
|
||||||
|
type source struct {
|
||||||
|
// Optional. Source file name. Depending on the runtime environment, this
|
||||||
|
// might be a simple name or a fully-qualified name.
|
||||||
|
File string `json:"file"`
|
||||||
|
|
||||||
|
// Optional. Line within the source file. 1-based; 0 indicates no line number
|
||||||
|
// available.
|
||||||
|
Line string `json:"line"`
|
||||||
|
|
||||||
|
// Optional. Human-readable name of the function or method being invoked, with
|
||||||
|
// optional context such as the class or package name. This information may be
|
||||||
|
// used in contexts such as the logs viewer, where a file and line number are
|
||||||
|
// less meaningful.
|
||||||
|
//
|
||||||
|
// The format should be dir/package.func.
|
||||||
|
Function string `json:"function"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalLogObject implements zapcore.ObjectMarshaller interface.
|
||||||
|
func (source source) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||||
|
enc.AddString("file", source.File)
|
||||||
|
enc.AddString("line", source.Line)
|
||||||
|
enc.AddString("function", source.Function)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSource(pc uintptr, file string, line int, ok bool) *source {
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var function string
|
||||||
|
if fn := runtime.FuncForPC(pc); fn != nil {
|
||||||
|
function = fn.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
source := &source{
|
||||||
|
File: file,
|
||||||
|
Line: strconv.Itoa(line),
|
||||||
|
Function: function,
|
||||||
|
}
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package zapdriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
traceKey = "logging.googleapis.com/trace"
|
||||||
|
spanKey = "logging.googleapis.com/spanId"
|
||||||
|
traceSampledKey = "logging.googleapis.com/trace_sampled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TraceContext adds the correct Stackdriver "trace", "span", "trace_sampled fields
|
||||||
|
//
|
||||||
|
// see: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
|
||||||
|
func TraceContext(trace string, spanId string, sampled bool, projectName string) []zap.Field {
|
||||||
|
return []zap.Field{
|
||||||
|
zap.String(traceKey, fmt.Sprintf("projects/%s/traces/%s", projectName, trace)),
|
||||||
|
zap.String(spanKey, spanId),
|
||||||
|
zap.Bool(traceSampledKey, sampled),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/blendle/zapdriver"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
|
@ -54,14 +55,14 @@ func NewLogger(configJSON string, levelOverride string, opts ...zap.Option) (*za
|
||||||
return enrichLoggerWithCommitID(logger), atomicLevel
|
return enrichLoggerWithCommitID(logger), atomicLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
loggingCfg := zap.NewProductionConfig()
|
loggingCfg := zapdriver.NewProductionConfig()
|
||||||
if levelOverride != "" {
|
if levelOverride != "" {
|
||||||
if level, err := levelFromString(levelOverride); err == nil {
|
if level, err := levelFromString(levelOverride); err == nil {
|
||||||
loggingCfg.Level = zap.NewAtomicLevelAt(*level)
|
loggingCfg.Level = zap.NewAtomicLevelAt(*level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger, err2 := loggingCfg.Build(opts...)
|
logger, err2 := loggingCfg.Build(append(opts, zapdriver.WrapCore())...)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
panic(err2)
|
panic(err2)
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +106,7 @@ func newLoggerFromConfig(configJSON string, levelOverride string, opts []zap.Opt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger, err := loggingCfg.Build(opts...)
|
logger, err := loggingCfg.Build(append(opts, zapdriver.WrapCore())...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, zap.AtomicLevel{}, err
|
return nil, zap.AtomicLevel{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -116,15 +117,13 @@ func newLoggerFromConfig(configJSON string, levelOverride string, opts []zap.Opt
|
||||||
}
|
}
|
||||||
|
|
||||||
func zapConfigFromJSON(configJSON string) (*zap.Config, error) {
|
func zapConfigFromJSON(configJSON string) (*zap.Config, error) {
|
||||||
if configJSON == "" {
|
loggingCfg := zapdriver.NewProductionConfig()
|
||||||
return nil, errEmptyLoggerConfig
|
if configJSON != "" {
|
||||||
}
|
if err := json.Unmarshal([]byte(configJSON), &loggingCfg); err != nil {
|
||||||
|
|
||||||
loggingCfg := &zap.Config{}
|
|
||||||
if err := json.Unmarshal([]byte(configJSON), loggingCfg); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return loggingCfg, nil
|
}
|
||||||
|
return &loggingCfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config contains the configuration defined in the logging ConfigMap.
|
// Config contains the configuration defined in the logging ConfigMap.
|
||||||
|
|
@ -134,30 +133,8 @@ type Config struct {
|
||||||
LoggingLevel map[string]zapcore.Level
|
LoggingLevel map[string]zapcore.Level
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultZLC = `{
|
|
||||||
"level": "info",
|
|
||||||
"development": false,
|
|
||||||
"outputPaths": ["stdout"],
|
|
||||||
"errorOutputPaths": ["stderr"],
|
|
||||||
"encoding": "json",
|
|
||||||
"encoderConfig": {
|
|
||||||
"timeKey": "ts",
|
|
||||||
"levelKey": "level",
|
|
||||||
"nameKey": "logger",
|
|
||||||
"callerKey": "caller",
|
|
||||||
"messageKey": "msg",
|
|
||||||
"stacktraceKey": "stacktrace",
|
|
||||||
"lineEnding": "",
|
|
||||||
"levelEncoder": "",
|
|
||||||
"timeEncoder": "iso8601",
|
|
||||||
"durationEncoder": "",
|
|
||||||
"callerEncoder": ""
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
func defaultConfig() *Config {
|
func defaultConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
LoggingConfig: defaultZLC,
|
|
||||||
LoggingLevel: make(map[string]zapcore.Level),
|
LoggingLevel: make(map[string]zapcore.Level),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ github.com/aws/aws-sdk-go/service/sts
|
||||||
github.com/aws/aws-sdk-go/service/sts/stsiface
|
github.com/aws/aws-sdk-go/service/sts/stsiface
|
||||||
# github.com/beorn7/perks v1.0.1
|
# github.com/beorn7/perks v1.0.1
|
||||||
github.com/beorn7/perks/quantile
|
github.com/beorn7/perks/quantile
|
||||||
|
# github.com/blendle/zapdriver v1.3.1
|
||||||
|
github.com/blendle/zapdriver
|
||||||
# github.com/census-instrumentation/opencensus-proto v0.3.0
|
# github.com/census-instrumentation/opencensus-proto v0.3.0
|
||||||
github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1
|
github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1
|
||||||
github.com/census-instrumentation/opencensus-proto/gen-go/agent/metrics/v1
|
github.com/census-instrumentation/opencensus-proto/gen-go/agent/metrics/v1
|
||||||
|
|
@ -843,7 +845,7 @@ k8s.io/kube-openapi/pkg/util/sets
|
||||||
k8s.io/utils/buffer
|
k8s.io/utils/buffer
|
||||||
k8s.io/utils/integer
|
k8s.io/utils/integer
|
||||||
k8s.io/utils/trace
|
k8s.io/utils/trace
|
||||||
# knative.dev/eventing v0.19.1-0.20201201072837-da18ee0a75f9
|
# knative.dev/eventing v0.19.1-0.20201202221809-1d3519c16565
|
||||||
## explicit
|
## explicit
|
||||||
knative.dev/eventing/pkg/apis/config
|
knative.dev/eventing/pkg/apis/config
|
||||||
knative.dev/eventing/pkg/apis/configs
|
knative.dev/eventing/pkg/apis/configs
|
||||||
|
|
@ -876,12 +878,12 @@ knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha2/fake
|
||||||
# knative.dev/hack v0.0.0-20201201234937-fddbf732e450
|
# knative.dev/hack v0.0.0-20201201234937-fddbf732e450
|
||||||
## explicit
|
## explicit
|
||||||
knative.dev/hack
|
knative.dev/hack
|
||||||
# knative.dev/networking v0.0.0-20201125133435-4b21f11ccfa7
|
# knative.dev/networking v0.0.0-20201203234509-4cd0793eed11
|
||||||
## explicit
|
## explicit
|
||||||
knative.dev/networking/pkg
|
knative.dev/networking/pkg
|
||||||
knative.dev/networking/pkg/apis/networking
|
knative.dev/networking/pkg/apis/networking
|
||||||
knative.dev/networking/pkg/apis/networking/v1alpha1
|
knative.dev/networking/pkg/apis/networking/v1alpha1
|
||||||
# knative.dev/pkg v0.0.0-20201130192436-e5346d90e980
|
# knative.dev/pkg v0.0.0-20201204013209-b89ac2a63293
|
||||||
## explicit
|
## explicit
|
||||||
knative.dev/pkg/apis
|
knative.dev/pkg/apis
|
||||||
knative.dev/pkg/apis/duck
|
knative.dev/pkg/apis/duck
|
||||||
|
|
@ -925,7 +927,7 @@ knative.dev/pkg/tracing/config
|
||||||
knative.dev/pkg/tracing/propagation
|
knative.dev/pkg/tracing/propagation
|
||||||
knative.dev/pkg/tracing/propagation/tracecontextb3
|
knative.dev/pkg/tracing/propagation/tracecontextb3
|
||||||
knative.dev/pkg/tracker
|
knative.dev/pkg/tracker
|
||||||
# knative.dev/serving v0.19.1-0.20201201235537-c8e2ccb33125
|
# knative.dev/serving v0.19.1-0.20201204004409-4bbc460995d6
|
||||||
## explicit
|
## explicit
|
||||||
knative.dev/serving/pkg/apis/autoscaling
|
knative.dev/serving/pkg/apis/autoscaling
|
||||||
knative.dev/serving/pkg/apis/autoscaling/v1alpha1
|
knative.dev/serving/pkg/apis/autoscaling/v1alpha1
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue