README
¶
Correlation logging instructions
There are three ways for using this package. All methods will result in a correlation ID being added to the log lines. Each method may make sense in different scenarios. We've provided all three methods as they can be used interchangeably depending on the developers preference.
Currently, a correlation context is created as part of a gRPC interceptor for the following openstorage packages:
- The CSI Driver (/csi)
- The SDK Server (/api/server/sdk)
Method 1: Per-package correlation logger
This method is nice if a package maintainer wants to register a global logging hook at a per-package level. This is nice as you only need to create the logger once per package. However, each log line can be quite verbose as you must provide WithContext(ctx)
File 1:
package example
var (
clogger := correlation.NewPackageLogger("test")
)
func testFunc(ctx context.Context ) {
ctx := correlation.WithCorrelationContext(ctx, "source-component")
clogger.WithContext(ctx).Info("test info log 1")
testFuncTwo(ctx)
}
File 2:
package example
func testFuncTwo(ctx context.Context) {
clogger.WithContext(ctx).Info("test info log 2")
}
Method 2: Per-function correlation logger
This method is great for reducing the amount of count needed per-line, as you do not need to pass in the context at every log line. However, you must remember to instantiate the logger at every function.
package example
func init() {
// RegisterComponent registers the package where this function was called from
// as a given component name.
correlation.RegisterComponent("example-package")
}
func testFunc() {
ctx := correlation.WithCorrelationContext(context.Background(), "test_origin")
clogger := correlation.NewFunctionLogger(ctx)
clogger.Info("test info log 1")
testFuncTwo(ctx)
}
func testFuncTwo(ctx context.Context) {
clogger := correlation.NewFunctionLogger(ctx)
clogger.Info("test info log")
}
Method 3: Global correlation logging hook
This is a nice catch-all for covering packages where a
package-level or function-level logger is not created. To mark certain packages as
a component, call RegisterComponent
from a given package. Each
log line in this package will have the registered component added. If a package
is not registered as a component, we'll find the component based on the package.
In main.go, first we should register the global hook:
package main
init() {
// RegisterGlobalHook will register the correlation logging
// hook at a global/multi-package level. Note that this is not
// component-aware and it is better to use NewFunctionLogger
// or NewPackageLogger for logging.
correlation.RegisterGlobalHook()
// RegisterComponent registers the package where this function was called from
// as a given component name.
correlation.RegisterComponent("main")
}
func main() {
...
}
In every other package, we can register components as we see fit.
This will help add extra context in our log lines for those who are unfamiliar with our codebase. For example, if a support engineer sees many log line failures with "csi-driver" component, they'll know who to contact regarding that failure.
The following example shows how a helper package can be registered as a component:
package example
func init() {
// RegisterComponent registers the package where this function was called from
// as a given component name.
correlation.RegisterComponent("example-package")
}
func TestFuncTwo(ctx context.Context) {
logrus.WithContext(ctx).Info("test info log 2")
}
Documentation
¶
Overview ¶
Copyright 2021 Portworx
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.
Copyright 2021 Portworx ¶
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.
Copyright 2021 Portworx ¶
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.
Index ¶
- Constants
- func ContextUnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, ...) error
- func NewFunctionLogger(ctx context.Context) *logrus.Logger
- func NewPackageLogger(component Component) *logrus.Logger
- func RegisterComponent(component Component)
- func RegisterGlobalHook()
- func TODO() context.Context
- func WithCorrelationContext(ctx context.Context, origin Component) context.Context
- type Component
- type ContextInterceptor
- type LogHook
- type RequestContext
Constants ¶
const ( // ContextKey represents the key for storing and retrieving // the correlation context in a context.Context object. ContextKey = contextKeyType("correlation-context") // ContextIDKey represents the key for the correlation ID ContextIDKey = "correlation-context-id" // ContextOriginKey represents the key for the correlation origin ContextOriginKey = "correlation-context-origin" ComponentUnknown = Component("unknown") ComponentCSIDriver = Component("csi-driver") ComponentSDK = Component("sdk-server") ComponentAuth = Component("openstorage/pkg/auth") )
const ( // LogFieldID represents a logging field for IDs LogFieldID = "correlation-id" // LogFieldID represents a logging field for the request origin LogFieldOrigin = "origin" // LogFieldComponent represents a logging field for control plane component. // This is typically set per-package and allows you see which component // the log originated from. LogFieldComponent = "component" )
Variables ¶
This section is empty.
Functions ¶
func ContextUnaryClientInterceptor ¶
func ContextUnaryClientInterceptor( ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error
ContextUnaryClientInterceptor creates a gRPC interceptor for adding correlation ID to each request
func NewFunctionLogger ¶
NewFunctionLogger creates a logger for usage at a per-function level For example, this logger can be instantiated inside of a function with a given context object. As logs are printed, they will automatically include the correlation context info.
func NewPackageLogger ¶
NewPackageLogger creates a package-level logger for a given component
func RegisterComponent ¶
func RegisterComponent(component Component)
RegisterComponent registers the package where this function was called from as a given component name. This should be done once per package where you want explicitly name the component for this package. Otherwise, we will detect the component based on package directory.
func RegisterGlobalHook ¶
func RegisterGlobalHook()
RegisterGlobalHook will register the correlation logging hook at a global/multi-package level per-program.
Types ¶
type Component ¶
type Component string
Component represents a control plane component for correlating requests
type ContextInterceptor ¶
type ContextInterceptor struct {
Origin Component
}
ContextInterceptor represents a correlation interceptor
func (*ContextInterceptor) ContextUnaryServerInterceptor ¶
func (ci *ContextInterceptor) ContextUnaryServerInterceptor( ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, ) (interface{}, error)
ContextUnaryServerInterceptor creates a gRPC interceptor for adding correlation ID to each request
type RequestContext ¶
type RequestContext struct { // ID is a randomly generated UUID per requst ID string // Origin is the starting point for this request. // Examples may include any of the following: // pxctl, pxc, kubernetes, CSI, SDK, etc Origin Component }
RequestContext represents the context for a given a request. A request represents a single action received from an SDK user, container orchestrator, or any other request.
func RequestContextFromContextMetadata ¶
func RequestContextFromContextMetadata(md metadata.MD) *RequestContext
RequestContextFromContextMetadata returns a new request context from a metadata object
func RequestContextFromContextValue ¶
func RequestContextFromContextValue(ctx context.Context) *RequestContext
RequestContextFromContextValue returns the request context from a context value
func (*RequestContext) AsMap ¶
func (rc *RequestContext) AsMap() map[string]string
AsMap returns the request context as a map