gcp/observability: implement public preview config syntax, logging schema, and exposed metrics (#5704)

This commit is contained in:
Zach Reyes 2022-10-12 15:18:49 -04:00 committed by GitHub
parent 8062981d4e
commit 8b3b10bd04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1929 additions and 2124 deletions

View File

@ -21,6 +21,7 @@ package observability
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
@ -28,79 +29,15 @@ import (
gcplogging "cloud.google.com/go/logging"
"golang.org/x/oauth2/google"
"google.golang.org/grpc/internal/envconfig"
)
const (
envObservabilityConfig = "GRPC_CONFIG_OBSERVABILITY"
envObservabilityConfigJSON = "GRPC_CONFIG_OBSERVABILITY_JSON"
envProjectID = "GOOGLE_CLOUD_PROJECT"
logFilterPatternRegexpStr = `^([\w./]+)/((?:\w+)|[*])$`
methodStringRegexpStr = `^([\w./]+)/((?:\w+)|[*])$`
)
var logFilterPatternRegexp = regexp.MustCompile(logFilterPatternRegexpStr)
// logFilter represents a method logging configuration.
type logFilter struct {
// Pattern is a string which can select a group of method names. By
// default, the Pattern is an empty string, matching no methods.
//
// Only "*" Wildcard is accepted for Pattern. A Pattern is in the form
// of <service>/<method> or just a character "*" .
//
// If the Pattern is "*", it specifies the defaults for all the
// services; If the Pattern is <service>/*, it specifies the defaults
// for all methods in the specified service <service>; If the Pattern is
// */<method>, this is not supported.
//
// Examples:
// - "Foo/Bar" selects only the method "Bar" from service "Foo"
// - "Foo/*" selects all methods from service "Foo"
// - "*" selects all methods from all services.
Pattern string `json:"pattern,omitempty"`
// HeaderBytes is the number of bytes of each header to log. If the size of
// the header is greater than the defined limit, content past the limit will
// be truncated. The default value is 0.
HeaderBytes int32 `json:"header_bytes,omitempty"`
// MessageBytes is the number of bytes of each message to log. If the size
// of the message is greater than the defined limit, content pass the limit
// will be truncated. The default value is 0.
MessageBytes int32 `json:"message_bytes,omitempty"`
}
// config is configuration for observability behaviors. By default, no
// configuration is required for tracing/metrics/logging to function. This
// config captures the most common knobs for gRPC users. It's always possible to
// override with explicit config in code.
type config struct {
// EnableCloudTrace represents whether the tracing data upload to
// CloudTrace should be enabled or not.
EnableCloudTrace bool `json:"enable_cloud_trace,omitempty"`
// EnableCloudMonitoring represents whether the metrics data upload to
// CloudMonitoring should be enabled or not.
EnableCloudMonitoring bool `json:"enable_cloud_monitoring,omitempty"`
// EnableCloudLogging represents Whether the logging data upload to
// CloudLogging should be enabled or not.
EnableCloudLogging bool `json:"enable_cloud_logging,omitempty"`
// DestinationProjectID is the destination GCP project identifier for the
// uploading log entries. If empty, the gRPC Observability plugin will
// attempt to fetch the project_id from the GCP environment variables, or
// from the default credentials.
DestinationProjectID string `json:"destination_project_id,omitempty"`
// LogFilters is a list of method config. The order matters here - the first
// Pattern which matches the current method will apply the associated config
// options in the logFilter. Any other logFilter that also matches that
// comes later will be ignored. So a logFilter of "*/*" should appear last
// in this list.
LogFilters []logFilter `json:"log_filters,omitempty"`
// GlobalTraceSamplingRate is the global setting that controls the
// probability of a RPC being traced. For example, 0.05 means there is a 5%
// chance for a RPC to be traced, 1.0 means trace every call, 0 means dont
// start new traces.
GlobalTraceSamplingRate float64 `json:"global_trace_sampling_rate,omitempty"`
// CustomTags a list of custom tags that will be attached to every log
// entry.
CustomTags map[string]string `json:"custom_tags,omitempty"`
}
var methodStringRegexp = regexp.MustCompile(methodStringRegexpStr)
// fetchDefaultProjectID fetches the default GCP project id from environment.
func fetchDefaultProjectID(ctx context.Context) string {
@ -123,14 +60,34 @@ func fetchDefaultProjectID(ctx context.Context) string {
return credentials.ProjectID
}
func validateFilters(config *config) error {
for _, filter := range config.LogFilters {
if filter.Pattern == "*" {
func validateLogEventMethod(methods []string, exclude bool) error {
for _, method := range methods {
if method == "*" {
if exclude {
return errors.New("cannot have exclude and a '*' wildcard")
}
continue
}
match := logFilterPatternRegexp.FindStringSubmatch(filter.Pattern)
match := methodStringRegexp.FindStringSubmatch(method)
if match == nil {
return fmt.Errorf("invalid log filter Pattern: %v", filter.Pattern)
return fmt.Errorf("invalid method string: %v", method)
}
}
return nil
}
func validateLoggingEvents(config *config) error {
if config.CloudLogging == nil {
return nil
}
for _, clientRPCEvent := range config.CloudLogging.ClientRPCEvents {
if err := validateLogEventMethod(clientRPCEvent.Methods, clientRPCEvent.Exclude); err != nil {
return fmt.Errorf("error in clientRPCEvent method: %v", err)
}
}
for _, serverRPCEvent := range config.CloudLogging.ServerRPCEvents {
if err := validateLogEventMethod(serverRPCEvent.Methods, serverRPCEvent.Exclude); err != nil {
return fmt.Errorf("error in serverRPCEvent method: %v", err)
}
}
return nil
@ -144,38 +101,161 @@ func unmarshalAndVerifyConfig(rawJSON json.RawMessage) (*config, error) {
if err := json.Unmarshal(rawJSON, &config); err != nil {
return nil, fmt.Errorf("error parsing observability config: %v", err)
}
if err := validateFilters(&config); err != nil {
if err := validateLoggingEvents(&config); err != nil {
return nil, fmt.Errorf("error parsing observability config: %v", err)
}
if config.GlobalTraceSamplingRate > 1 || config.GlobalTraceSamplingRate < 0 {
return nil, fmt.Errorf("error parsing observability config: invalid global trace sampling rate %v", config.GlobalTraceSamplingRate)
if config.CloudTrace != nil && (config.CloudTrace.SamplingRate > 1 || config.CloudTrace.SamplingRate < 0) {
return nil, fmt.Errorf("error parsing observability config: invalid cloud trace sampling rate %v", config.CloudTrace.SamplingRate)
}
logger.Infof("Parsed ObservabilityConfig: %+v", &config)
return &config, nil
}
func parseObservabilityConfig() (*config, error) {
if fileSystemPath := os.Getenv(envObservabilityConfigJSON); fileSystemPath != "" {
content, err := ioutil.ReadFile(fileSystemPath) // TODO: Switch to os.ReadFile once dropped support for go 1.15
if f := envconfig.ObservabilityConfigFile; f != "" {
if envconfig.ObservabilityConfig != "" {
logger.Warning("Ignoring GRPC_GCP_OBSERVABILITY_CONFIG and using GRPC_GCP_OBSERVABILITY_CONFIG_FILE contents.")
}
content, err := ioutil.ReadFile(f) // TODO: Switch to os.ReadFile once dropped support for go 1.15
if err != nil {
return nil, fmt.Errorf("error reading observability configuration file %q: %v", fileSystemPath, err)
return nil, fmt.Errorf("error reading observability configuration file %q: %v", f, err)
}
return unmarshalAndVerifyConfig(content)
} else if content := os.Getenv(envObservabilityConfig); content != "" {
return unmarshalAndVerifyConfig([]byte(content))
} else if envconfig.ObservabilityConfig != "" {
return unmarshalAndVerifyConfig([]byte(envconfig.ObservabilityConfig))
}
// If the ENV var doesn't exist, do nothing
return nil, nil
}
func ensureProjectIDInObservabilityConfig(ctx context.Context, config *config) error {
if config.DestinationProjectID == "" {
if config.ProjectID == "" {
// Try to fetch the GCP project id
projectID := fetchDefaultProjectID(ctx)
if projectID == "" {
return fmt.Errorf("empty destination project ID")
}
config.DestinationProjectID = projectID
config.ProjectID = projectID
}
return nil
}
type clientRPCEvents struct {
// Methods is a list of strings which can select a group of methods. By
// default, the list is empty, matching no methods.
//
// The value of the method is in the form of <service>/<method>.
//
// "*" is accepted as a wildcard for:
// 1. The method name. If the value is <service>/*, it matches all
// methods in the specified service.
// 2. The whole value of the field which matches any <service>/<method>.
// Its not supported when Exclude is true.
// 3. The * wildcard cannot be used on the service name independently,
// */<method> is not supported.
//
// The service name, when specified, must be the fully qualified service
// name, including the package name.
//
// Examples:
// 1."goo.Foo/Bar" selects only the method "Bar" from service "goo.Foo",
// here “goo” is the package name.
// 2."goo.Foo/*" selects all methods from service "goo.Foo"
// 3. "*" selects all methods from all services.
Methods []string `json:"method,omitempty"`
// Exclude represents whether the methods denoted by Methods should be
// excluded from logging. The default value is false, meaning the methods
// denoted by Methods are included in the logging. If Exclude is true, the
// wildcard `*` cannot be used as value of an entry in Methods.
Exclude bool `json:"exclude,omitempty"`
// MaxMetadataBytes is the maximum number of bytes of each header to log. If
// the size of the metadata is greater than the defined limit, content past
// the limit will be truncated. The default value is 0.
MaxMetadataBytes int `json:"max_metadata_bytes"`
// MaxMessageBytes is the maximum number of bytes of each message to log. If
// the size of the message is greater than the defined limit, content past
// the limit will be truncated. The default value is 0.
MaxMessageBytes int `json:"max_message_bytes"`
}
type serverRPCEvents struct {
// Methods is a list of strings which can select a group of methods. By
// default, the list is empty, matching no methods.
//
// The value of the method is in the form of <service>/<method>.
//
// "*" is accepted as a wildcard for:
// 1. The method name. If the value is <service>/*, it matches all
// methods in the specified service.
// 2. The whole value of the field which matches any <service>/<method>.
// Its not supported when Exclude is true.
// 3. The * wildcard cannot be used on the service name independently,
// */<method> is not supported.
//
// The service name, when specified, must be the fully qualified service
// name, including the package name.
//
// Examples:
// 1."goo.Foo/Bar" selects only the method "Bar" from service "goo.Foo",
// here “goo” is the package name.
// 2."goo.Foo/*" selects all methods from service "goo.Foo"
// 3. "*" selects all methods from all services.
Methods []string `json:"method,omitempty"`
// Exclude represents whether the methods denoted by Methods should be
// excluded from logging. The default value is false, meaning the methods
// denoted by Methods are included in the logging. If Exclude is true, the
// wildcard `*` cannot be used as value of an entry in Methods.
Exclude bool `json:"exclude,omitempty"`
// MaxMetadataBytes is the maximum number of bytes of each header to log. If
// the size of the metadata is greater than the defined limit, content past
// the limit will be truncated. The default value is 0.
MaxMetadataBytes int `json:"max_metadata_bytes"`
// MaxMessageBytes is the maximum number of bytes of each message to log. If
// the size of the message is greater than the defined limit, content past
// the limit will be truncated. The default value is 0.
MaxMessageBytes int `json:"max_message_bytes"`
}
type cloudLogging struct {
// ClientRPCEvents represents the configuration for outgoing RPC's from the
// binary. The client_rpc_events configs are evaluated in text order, the
// first one matched is used. If an RPC doesn't match an entry, it will
// continue on to the next entry in the list.
ClientRPCEvents []clientRPCEvents `json:"client_rpc_events,omitempty"`
// ServerRPCEvents represents the configuration for incoming RPC's to the
// binary. The server_rpc_events configs are evaluated in text order, the
// first one matched is used. If an RPC doesn't match an entry, it will
// continue on to the next entry in the list.
ServerRPCEvents []serverRPCEvents `json:"server_rpc_events,omitempty"`
}
type cloudMonitoring struct{}
type cloudTrace struct {
// SamplingRate is the global setting that controls the probability of a RPC
// being traced. For example, 0.05 means there is a 5% chance for a RPC to
// be traced, 1.0 means trace every call, 0 means dont start new traces. By
// default, the sampling_rate is 0.
SamplingRate float64 `json:"sampling_rate,omitempty"`
}
type config struct {
// ProjectID is the destination GCP project identifier for uploading log
// entries. If empty, the gRPC Observability plugin will attempt to fetch
// the project_id from the GCP environment variables, or from the default
// credentials. If not found, the observability init functions will return
// an error.
ProjectID string `json:"project_id,omitempty"`
// CloudLogging defines the logging options. If not present, logging is disabled.
CloudLogging *cloudLogging `json:"cloud_logging,omitempty"`
// CloudMonitoring determines whether or not metrics are enabled based on
// whether it is present or not. If present, monitoring will be enabled, if
// not present, monitoring is disabled.
CloudMonitoring *cloudMonitoring `json:"cloud_monitoring,omitempty"`
// CloudTrace defines the tracing options. When present, tracing is enabled
// with default configurations. When absent, the tracing is disabled.
CloudTrace *cloudTrace `json:"cloud_trace,omitempty"`
// Labels are applied to cloud logging, monitoring, and trace.
Labels map[string]string `json:"labels,omitempty"`
}

View File

@ -20,12 +20,9 @@ package observability
import (
"context"
"encoding/json"
"fmt"
gcplogging "cloud.google.com/go/logging"
grpclogrecordpb "google.golang.org/grpc/gcp/observability/internal/logging"
"google.golang.org/protobuf/encoding/protojson"
)
// loggingExporter is the interface of logging exporter for gRPC Observability.
@ -33,7 +30,7 @@ import (
// now, it exists for testing purposes.
type loggingExporter interface {
// EmitGrpcLogRecord writes a gRPC LogRecord to cache without blocking.
EmitGrpcLogRecord(*grpclogrecordpb.GrpcLogRecord)
EmitGcpLoggingEntry(entry gcplogging.Entry)
// Close flushes all pending data and closes the exporter.
Close() error
}
@ -44,58 +41,23 @@ type cloudLoggingExporter struct {
logger *gcplogging.Logger
}
func newCloudLoggingExporter(ctx context.Context, config *config) (*cloudLoggingExporter, error) {
c, err := gcplogging.NewClient(ctx, fmt.Sprintf("projects/%v", config.DestinationProjectID))
func newCloudLoggingExporter(ctx context.Context, config *config) (loggingExporter, error) {
c, err := gcplogging.NewClient(ctx, fmt.Sprintf("projects/%v", config.ProjectID))
if err != nil {
return nil, fmt.Errorf("failed to create cloudLoggingExporter: %v", err)
}
defer logger.Infof("Successfully created cloudLoggingExporter")
if len(config.CustomTags) != 0 {
logger.Infof("Adding custom tags: %+v", config.CustomTags)
if len(config.Labels) != 0 {
logger.Infof("Adding labels: %+v", config.Labels)
}
return &cloudLoggingExporter{
projectID: config.DestinationProjectID,
projectID: config.ProjectID,
client: c,
logger: c.Logger("microservices.googleapis.com/observability/grpc", gcplogging.CommonLabels(config.CustomTags)),
logger: c.Logger("microservices.googleapis.com/observability/grpc", gcplogging.CommonLabels(config.Labels)),
}, nil
}
// mapLogLevelToSeverity maps the gRPC defined log level to Cloud Logging's
// Severity. The canonical definition can be found at
// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity.
var logLevelToSeverity = map[grpclogrecordpb.GrpcLogRecord_LogLevel]gcplogging.Severity{
grpclogrecordpb.GrpcLogRecord_LOG_LEVEL_UNKNOWN: 0,
grpclogrecordpb.GrpcLogRecord_LOG_LEVEL_TRACE: 100, // Cloud Logging doesn't have a trace level, treated as DEBUG.
grpclogrecordpb.GrpcLogRecord_LOG_LEVEL_DEBUG: 100,
grpclogrecordpb.GrpcLogRecord_LOG_LEVEL_INFO: 200,
grpclogrecordpb.GrpcLogRecord_LOG_LEVEL_WARN: 400,
grpclogrecordpb.GrpcLogRecord_LOG_LEVEL_ERROR: 500,
grpclogrecordpb.GrpcLogRecord_LOG_LEVEL_CRITICAL: 600,
}
var protoToJSONOptions = &protojson.MarshalOptions{
UseProtoNames: true,
UseEnumNumbers: false,
}
func (cle *cloudLoggingExporter) EmitGrpcLogRecord(l *grpclogrecordpb.GrpcLogRecord) {
// Converts the log record content to a more readable format via protojson.
jsonBytes, err := protoToJSONOptions.Marshal(l)
if err != nil {
logger.Infof("Unable to marshal log record: %v", l)
return
}
var payload map[string]interface{}
err = json.Unmarshal(jsonBytes, &payload)
if err != nil {
logger.Infof("Unable to unmarshal bytes to JSON: %v", jsonBytes)
return
}
entry := gcplogging.Entry{
Timestamp: l.Timestamp.AsTime(),
Severity: logLevelToSeverity[l.LogLevel],
Payload: payload,
}
func (cle *cloudLoggingExporter) EmitGcpLoggingEntry(entry gcplogging.Entry) {
cle.logger.Log(entry)
if logger.V(2) {
logger.Infof("Uploading event to CloudLogging: %+v", entry)

View File

@ -5,12 +5,11 @@ go 1.14
require (
cloud.google.com/go/logging v1.4.2
contrib.go.opencensus.io/exporter/stackdriver v0.13.12
github.com/golang/protobuf v1.5.2
github.com/google/go-cmp v0.5.6
github.com/google/uuid v1.3.0
go.opencensus.io v0.23.0
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
google.golang.org/grpc v1.46.0
google.golang.org/protobuf v1.27.1
)
// TODO(lidiz) remove the following line when we have a release containing the

View File

@ -1,914 +0,0 @@
// Copyright 2022 The gRPC 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.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: gcp/observability/internal/logging/logging.proto
package logging
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
durationpb "google.golang.org/protobuf/types/known/durationpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// List of event types
type GrpcLogRecord_EventType int32
const (
// Unknown event type
GrpcLogRecord_GRPC_CALL_UNKNOWN GrpcLogRecord_EventType = 0
// Header sent from client to server
GrpcLogRecord_GRPC_CALL_REQUEST_HEADER GrpcLogRecord_EventType = 1
// Header sent from server to client
GrpcLogRecord_GRPC_CALL_RESPONSE_HEADER GrpcLogRecord_EventType = 2
// Message sent from client to server
GrpcLogRecord_GRPC_CALL_REQUEST_MESSAGE GrpcLogRecord_EventType = 3
// Message sent from server to client
GrpcLogRecord_GRPC_CALL_RESPONSE_MESSAGE GrpcLogRecord_EventType = 4
// Trailer indicates the end of the gRPC call
GrpcLogRecord_GRPC_CALL_TRAILER GrpcLogRecord_EventType = 5
// A signal that client is done sending
GrpcLogRecord_GRPC_CALL_HALF_CLOSE GrpcLogRecord_EventType = 6
// A signal that the rpc is canceled
GrpcLogRecord_GRPC_CALL_CANCEL GrpcLogRecord_EventType = 7
)
// Enum value maps for GrpcLogRecord_EventType.
var (
GrpcLogRecord_EventType_name = map[int32]string{
0: "GRPC_CALL_UNKNOWN",
1: "GRPC_CALL_REQUEST_HEADER",
2: "GRPC_CALL_RESPONSE_HEADER",
3: "GRPC_CALL_REQUEST_MESSAGE",
4: "GRPC_CALL_RESPONSE_MESSAGE",
5: "GRPC_CALL_TRAILER",
6: "GRPC_CALL_HALF_CLOSE",
7: "GRPC_CALL_CANCEL",
}
GrpcLogRecord_EventType_value = map[string]int32{
"GRPC_CALL_UNKNOWN": 0,
"GRPC_CALL_REQUEST_HEADER": 1,
"GRPC_CALL_RESPONSE_HEADER": 2,
"GRPC_CALL_REQUEST_MESSAGE": 3,
"GRPC_CALL_RESPONSE_MESSAGE": 4,
"GRPC_CALL_TRAILER": 5,
"GRPC_CALL_HALF_CLOSE": 6,
"GRPC_CALL_CANCEL": 7,
}
)
func (x GrpcLogRecord_EventType) Enum() *GrpcLogRecord_EventType {
p := new(GrpcLogRecord_EventType)
*p = x
return p
}
func (x GrpcLogRecord_EventType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (GrpcLogRecord_EventType) Descriptor() protoreflect.EnumDescriptor {
return file_gcp_observability_internal_logging_logging_proto_enumTypes[0].Descriptor()
}
func (GrpcLogRecord_EventType) Type() protoreflect.EnumType {
return &file_gcp_observability_internal_logging_logging_proto_enumTypes[0]
}
func (x GrpcLogRecord_EventType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use GrpcLogRecord_EventType.Descriptor instead.
func (GrpcLogRecord_EventType) EnumDescriptor() ([]byte, []int) {
return file_gcp_observability_internal_logging_logging_proto_rawDescGZIP(), []int{0, 0}
}
// The entity that generates the log entry
type GrpcLogRecord_EventLogger int32
const (
GrpcLogRecord_LOGGER_UNKNOWN GrpcLogRecord_EventLogger = 0
GrpcLogRecord_LOGGER_CLIENT GrpcLogRecord_EventLogger = 1
GrpcLogRecord_LOGGER_SERVER GrpcLogRecord_EventLogger = 2
)
// Enum value maps for GrpcLogRecord_EventLogger.
var (
GrpcLogRecord_EventLogger_name = map[int32]string{
0: "LOGGER_UNKNOWN",
1: "LOGGER_CLIENT",
2: "LOGGER_SERVER",
}
GrpcLogRecord_EventLogger_value = map[string]int32{
"LOGGER_UNKNOWN": 0,
"LOGGER_CLIENT": 1,
"LOGGER_SERVER": 2,
}
)
func (x GrpcLogRecord_EventLogger) Enum() *GrpcLogRecord_EventLogger {
p := new(GrpcLogRecord_EventLogger)
*p = x
return p
}
func (x GrpcLogRecord_EventLogger) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (GrpcLogRecord_EventLogger) Descriptor() protoreflect.EnumDescriptor {
return file_gcp_observability_internal_logging_logging_proto_enumTypes[1].Descriptor()
}
func (GrpcLogRecord_EventLogger) Type() protoreflect.EnumType {
return &file_gcp_observability_internal_logging_logging_proto_enumTypes[1]
}
func (x GrpcLogRecord_EventLogger) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use GrpcLogRecord_EventLogger.Descriptor instead.
func (GrpcLogRecord_EventLogger) EnumDescriptor() ([]byte, []int) {
return file_gcp_observability_internal_logging_logging_proto_rawDescGZIP(), []int{0, 1}
}
// The log severity level of the log entry
type GrpcLogRecord_LogLevel int32
const (
GrpcLogRecord_LOG_LEVEL_UNKNOWN GrpcLogRecord_LogLevel = 0
GrpcLogRecord_LOG_LEVEL_TRACE GrpcLogRecord_LogLevel = 1
GrpcLogRecord_LOG_LEVEL_DEBUG GrpcLogRecord_LogLevel = 2
GrpcLogRecord_LOG_LEVEL_INFO GrpcLogRecord_LogLevel = 3
GrpcLogRecord_LOG_LEVEL_WARN GrpcLogRecord_LogLevel = 4
GrpcLogRecord_LOG_LEVEL_ERROR GrpcLogRecord_LogLevel = 5
GrpcLogRecord_LOG_LEVEL_CRITICAL GrpcLogRecord_LogLevel = 6
)
// Enum value maps for GrpcLogRecord_LogLevel.
var (
GrpcLogRecord_LogLevel_name = map[int32]string{
0: "LOG_LEVEL_UNKNOWN",
1: "LOG_LEVEL_TRACE",
2: "LOG_LEVEL_DEBUG",
3: "LOG_LEVEL_INFO",
4: "LOG_LEVEL_WARN",
5: "LOG_LEVEL_ERROR",
6: "LOG_LEVEL_CRITICAL",
}
GrpcLogRecord_LogLevel_value = map[string]int32{
"LOG_LEVEL_UNKNOWN": 0,
"LOG_LEVEL_TRACE": 1,
"LOG_LEVEL_DEBUG": 2,
"LOG_LEVEL_INFO": 3,
"LOG_LEVEL_WARN": 4,
"LOG_LEVEL_ERROR": 5,
"LOG_LEVEL_CRITICAL": 6,
}
)
func (x GrpcLogRecord_LogLevel) Enum() *GrpcLogRecord_LogLevel {
p := new(GrpcLogRecord_LogLevel)
*p = x
return p
}
func (x GrpcLogRecord_LogLevel) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (GrpcLogRecord_LogLevel) Descriptor() protoreflect.EnumDescriptor {
return file_gcp_observability_internal_logging_logging_proto_enumTypes[2].Descriptor()
}
func (GrpcLogRecord_LogLevel) Type() protoreflect.EnumType {
return &file_gcp_observability_internal_logging_logging_proto_enumTypes[2]
}
func (x GrpcLogRecord_LogLevel) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use GrpcLogRecord_LogLevel.Descriptor instead.
func (GrpcLogRecord_LogLevel) EnumDescriptor() ([]byte, []int) {
return file_gcp_observability_internal_logging_logging_proto_rawDescGZIP(), []int{0, 2}
}
type GrpcLogRecord_Address_Type int32
const (
GrpcLogRecord_Address_TYPE_UNKNOWN GrpcLogRecord_Address_Type = 0
GrpcLogRecord_Address_TYPE_IPV4 GrpcLogRecord_Address_Type = 1 // in 1.2.3.4 form
GrpcLogRecord_Address_TYPE_IPV6 GrpcLogRecord_Address_Type = 2 // IPv6 canonical form (RFC5952 section 4)
GrpcLogRecord_Address_TYPE_UNIX GrpcLogRecord_Address_Type = 3 // UDS string
)
// Enum value maps for GrpcLogRecord_Address_Type.
var (
GrpcLogRecord_Address_Type_name = map[int32]string{
0: "TYPE_UNKNOWN",
1: "TYPE_IPV4",
2: "TYPE_IPV6",
3: "TYPE_UNIX",
}
GrpcLogRecord_Address_Type_value = map[string]int32{
"TYPE_UNKNOWN": 0,
"TYPE_IPV4": 1,
"TYPE_IPV6": 2,
"TYPE_UNIX": 3,
}
)
func (x GrpcLogRecord_Address_Type) Enum() *GrpcLogRecord_Address_Type {
p := new(GrpcLogRecord_Address_Type)
*p = x
return p
}
func (x GrpcLogRecord_Address_Type) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (GrpcLogRecord_Address_Type) Descriptor() protoreflect.EnumDescriptor {
return file_gcp_observability_internal_logging_logging_proto_enumTypes[3].Descriptor()
}
func (GrpcLogRecord_Address_Type) Type() protoreflect.EnumType {
return &file_gcp_observability_internal_logging_logging_proto_enumTypes[3]
}
func (x GrpcLogRecord_Address_Type) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use GrpcLogRecord_Address_Type.Descriptor instead.
func (GrpcLogRecord_Address_Type) EnumDescriptor() ([]byte, []int) {
return file_gcp_observability_internal_logging_logging_proto_rawDescGZIP(), []int{0, 2, 0}
}
type GrpcLogRecord struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The timestamp of the log event
Timestamp *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
// Uniquely identifies a call. The value must not be 0 in order to disambiguate
// from an unset value.
// Each call may have several log entries. They will all have the same rpc_id.
// Nothing is guaranteed about their value other than they are unique across
// different RPCs in the same gRPC process.
RpcId string `protobuf:"bytes,2,opt,name=rpc_id,json=rpcId,proto3" json:"rpc_id,omitempty"`
EventType GrpcLogRecord_EventType `protobuf:"varint,3,opt,name=event_type,json=eventType,proto3,enum=grpc.observability.logging.v1.GrpcLogRecord_EventType" json:"event_type,omitempty"` // one of the above EventType enum
EventLogger GrpcLogRecord_EventLogger `protobuf:"varint,4,opt,name=event_logger,json=eventLogger,proto3,enum=grpc.observability.logging.v1.GrpcLogRecord_EventLogger" json:"event_logger,omitempty"` // one of the above EventLogger enum
// the name of the service
ServiceName string `protobuf:"bytes,5,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"`
// the name of the RPC method
MethodName string `protobuf:"bytes,6,opt,name=method_name,json=methodName,proto3" json:"method_name,omitempty"`
LogLevel GrpcLogRecord_LogLevel `protobuf:"varint,7,opt,name=log_level,json=logLevel,proto3,enum=grpc.observability.logging.v1.GrpcLogRecord_LogLevel" json:"log_level,omitempty"` // one of the above LogLevel enum
// Peer address information. On client side, peer is logged on server
// header event or trailer event (if trailer-only). On server side, peer
// is always logged on the client header event.
PeerAddress *GrpcLogRecord_Address `protobuf:"bytes,8,opt,name=peer_address,json=peerAddress,proto3" json:"peer_address,omitempty"`
// the RPC timeout value
Timeout *durationpb.Duration `protobuf:"bytes,11,opt,name=timeout,proto3" json:"timeout,omitempty"`
// A single process may be used to run multiple virtual servers with
// different identities.
// The authority is the name of such a server identify. It is typically a
// portion of the URI in the form of <host> or <host>:<port>.
Authority string `protobuf:"bytes,12,opt,name=authority,proto3" json:"authority,omitempty"`
// Size of the message or metadata, depending on the event type,
// regardless of whether the full message or metadata is being logged
// (i.e. could be truncated or omitted).
PayloadSize uint32 `protobuf:"varint,13,opt,name=payload_size,json=payloadSize,proto3" json:"payload_size,omitempty"`
// true if message or metadata field is either truncated or omitted due
// to config options
PayloadTruncated bool `protobuf:"varint,14,opt,name=payload_truncated,json=payloadTruncated,proto3" json:"payload_truncated,omitempty"`
// Used by header event or trailer event
Metadata *GrpcLogRecord_Metadata `protobuf:"bytes,15,opt,name=metadata,proto3" json:"metadata,omitempty"`
// The entry sequence ID for this call. The first message has a value of 1,
// to disambiguate from an unset value. The purpose of this field is to
// detect missing entries in environments where durability or ordering is
// not guaranteed.
SequenceId uint64 `protobuf:"varint,16,opt,name=sequence_id,json=sequenceId,proto3" json:"sequence_id,omitempty"`
// Used by message event
Message []byte `protobuf:"bytes,17,opt,name=message,proto3" json:"message,omitempty"`
// The gRPC status code
StatusCode uint32 `protobuf:"varint,18,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"`
// The gRPC status message
StatusMessage string `protobuf:"bytes,19,opt,name=status_message,json=statusMessage,proto3" json:"status_message,omitempty"`
// The value of the grpc-status-details-bin metadata key, if any.
// This is always an encoded google.rpc.Status message
StatusDetails []byte `protobuf:"bytes,20,opt,name=status_details,json=statusDetails,proto3" json:"status_details,omitempty"`
}
func (x *GrpcLogRecord) Reset() {
*x = GrpcLogRecord{}
if protoimpl.UnsafeEnabled {
mi := &file_gcp_observability_internal_logging_logging_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GrpcLogRecord) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GrpcLogRecord) ProtoMessage() {}
func (x *GrpcLogRecord) ProtoReflect() protoreflect.Message {
mi := &file_gcp_observability_internal_logging_logging_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GrpcLogRecord.ProtoReflect.Descriptor instead.
func (*GrpcLogRecord) Descriptor() ([]byte, []int) {
return file_gcp_observability_internal_logging_logging_proto_rawDescGZIP(), []int{0}
}
func (x *GrpcLogRecord) GetTimestamp() *timestamppb.Timestamp {
if x != nil {
return x.Timestamp
}
return nil
}
func (x *GrpcLogRecord) GetRpcId() string {
if x != nil {
return x.RpcId
}
return ""
}
func (x *GrpcLogRecord) GetEventType() GrpcLogRecord_EventType {
if x != nil {
return x.EventType
}
return GrpcLogRecord_GRPC_CALL_UNKNOWN
}
func (x *GrpcLogRecord) GetEventLogger() GrpcLogRecord_EventLogger {
if x != nil {
return x.EventLogger
}
return GrpcLogRecord_LOGGER_UNKNOWN
}
func (x *GrpcLogRecord) GetServiceName() string {
if x != nil {
return x.ServiceName
}
return ""
}
func (x *GrpcLogRecord) GetMethodName() string {
if x != nil {
return x.MethodName
}
return ""
}
func (x *GrpcLogRecord) GetLogLevel() GrpcLogRecord_LogLevel {
if x != nil {
return x.LogLevel
}
return GrpcLogRecord_LOG_LEVEL_UNKNOWN
}
func (x *GrpcLogRecord) GetPeerAddress() *GrpcLogRecord_Address {
if x != nil {
return x.PeerAddress
}
return nil
}
func (x *GrpcLogRecord) GetTimeout() *durationpb.Duration {
if x != nil {
return x.Timeout
}
return nil
}
func (x *GrpcLogRecord) GetAuthority() string {
if x != nil {
return x.Authority
}
return ""
}
func (x *GrpcLogRecord) GetPayloadSize() uint32 {
if x != nil {
return x.PayloadSize
}
return 0
}
func (x *GrpcLogRecord) GetPayloadTruncated() bool {
if x != nil {
return x.PayloadTruncated
}
return false
}
func (x *GrpcLogRecord) GetMetadata() *GrpcLogRecord_Metadata {
if x != nil {
return x.Metadata
}
return nil
}
func (x *GrpcLogRecord) GetSequenceId() uint64 {
if x != nil {
return x.SequenceId
}
return 0
}
func (x *GrpcLogRecord) GetMessage() []byte {
if x != nil {
return x.Message
}
return nil
}
func (x *GrpcLogRecord) GetStatusCode() uint32 {
if x != nil {
return x.StatusCode
}
return 0
}
func (x *GrpcLogRecord) GetStatusMessage() string {
if x != nil {
return x.StatusMessage
}
return ""
}
func (x *GrpcLogRecord) GetStatusDetails() []byte {
if x != nil {
return x.StatusDetails
}
return nil
}
// A list of metadata pairs
type GrpcLogRecord_Metadata struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Entry []*GrpcLogRecord_MetadataEntry `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"`
}
func (x *GrpcLogRecord_Metadata) Reset() {
*x = GrpcLogRecord_Metadata{}
if protoimpl.UnsafeEnabled {
mi := &file_gcp_observability_internal_logging_logging_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GrpcLogRecord_Metadata) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GrpcLogRecord_Metadata) ProtoMessage() {}
func (x *GrpcLogRecord_Metadata) ProtoReflect() protoreflect.Message {
mi := &file_gcp_observability_internal_logging_logging_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GrpcLogRecord_Metadata.ProtoReflect.Descriptor instead.
func (*GrpcLogRecord_Metadata) Descriptor() ([]byte, []int) {
return file_gcp_observability_internal_logging_logging_proto_rawDescGZIP(), []int{0, 0}
}
func (x *GrpcLogRecord_Metadata) GetEntry() []*GrpcLogRecord_MetadataEntry {
if x != nil {
return x.Entry
}
return nil
}
// One metadata key value pair
type GrpcLogRecord_MetadataEntry struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *GrpcLogRecord_MetadataEntry) Reset() {
*x = GrpcLogRecord_MetadataEntry{}
if protoimpl.UnsafeEnabled {
mi := &file_gcp_observability_internal_logging_logging_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GrpcLogRecord_MetadataEntry) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GrpcLogRecord_MetadataEntry) ProtoMessage() {}
func (x *GrpcLogRecord_MetadataEntry) ProtoReflect() protoreflect.Message {
mi := &file_gcp_observability_internal_logging_logging_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GrpcLogRecord_MetadataEntry.ProtoReflect.Descriptor instead.
func (*GrpcLogRecord_MetadataEntry) Descriptor() ([]byte, []int) {
return file_gcp_observability_internal_logging_logging_proto_rawDescGZIP(), []int{0, 1}
}
func (x *GrpcLogRecord_MetadataEntry) GetKey() string {
if x != nil {
return x.Key
}
return ""
}
func (x *GrpcLogRecord_MetadataEntry) GetValue() []byte {
if x != nil {
return x.Value
}
return nil
}
// Address information
type GrpcLogRecord_Address struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type GrpcLogRecord_Address_Type `protobuf:"varint,1,opt,name=type,proto3,enum=grpc.observability.logging.v1.GrpcLogRecord_Address_Type" json:"type,omitempty"`
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
// only for TYPE_IPV4 and TYPE_IPV6
IpPort uint32 `protobuf:"varint,3,opt,name=ip_port,json=ipPort,proto3" json:"ip_port,omitempty"`
}
func (x *GrpcLogRecord_Address) Reset() {
*x = GrpcLogRecord_Address{}
if protoimpl.UnsafeEnabled {
mi := &file_gcp_observability_internal_logging_logging_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GrpcLogRecord_Address) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GrpcLogRecord_Address) ProtoMessage() {}
func (x *GrpcLogRecord_Address) ProtoReflect() protoreflect.Message {
mi := &file_gcp_observability_internal_logging_logging_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GrpcLogRecord_Address.ProtoReflect.Descriptor instead.
func (*GrpcLogRecord_Address) Descriptor() ([]byte, []int) {
return file_gcp_observability_internal_logging_logging_proto_rawDescGZIP(), []int{0, 2}
}
func (x *GrpcLogRecord_Address) GetType() GrpcLogRecord_Address_Type {
if x != nil {
return x.Type
}
return GrpcLogRecord_Address_TYPE_UNKNOWN
}
func (x *GrpcLogRecord_Address) GetAddress() string {
if x != nil {
return x.Address
}
return ""
}
func (x *GrpcLogRecord_Address) GetIpPort() uint32 {
if x != nil {
return x.IpPort
}
return 0
}
var File_gcp_observability_internal_logging_logging_proto protoreflect.FileDescriptor
var file_gcp_observability_internal_logging_logging_proto_rawDesc = []byte{
0x0a, 0x30, 0x67, 0x63, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c,
0x69, 0x74, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x6f, 0x67,
0x67, 0x69, 0x6e, 0x67, 0x2f, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x1d, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61,
0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x2e, 0x76,
0x31, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x22, 0xe5, 0x0d, 0x0a, 0x0d, 0x47, 0x72, 0x70, 0x63, 0x4c, 0x6f, 0x67, 0x52, 0x65,
0x63, 0x6f, 0x72, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x15,
0x0a, 0x06, 0x72, 0x70, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
0x72, 0x70, 0x63, 0x49, 0x64, 0x12, 0x55, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74,
0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x67, 0x72, 0x70, 0x63,
0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x6c,
0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x70, 0x63, 0x4c, 0x6f,
0x67, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x5b, 0x0a, 0x0c,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x38, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76,
0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x2e,
0x76, 0x31, 0x2e, 0x47, 0x72, 0x70, 0x63, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64,
0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x52, 0x0b, 0x65, 0x76,
0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b,
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x52, 0x0a,
0x09, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e,
0x32, 0x35, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62,
0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31,
0x2e, 0x47, 0x72, 0x70, 0x63, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x4c,
0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65,
0x6c, 0x12, 0x57, 0x0a, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6f,
0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x6c, 0x6f, 0x67,
0x67, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x70, 0x63, 0x4c, 0x6f, 0x67, 0x52,
0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0b, 0x70,
0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69,
0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12,
0x1c, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20, 0x01,
0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x21, 0x0a,
0x0c, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0d, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65,
0x12, 0x2b, 0x0a, 0x11, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x74, 0x72, 0x75, 0x6e,
0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x70, 0x61, 0x79,
0x6c, 0x6f, 0x61, 0x64, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x51, 0x0a,
0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x35, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x69,
0x6c, 0x69, 0x74, 0x79, 0x2e, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e,
0x47, 0x72, 0x70, 0x63, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x4d, 0x65,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18,
0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x49,
0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x11, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73,
0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x25, 0x0a, 0x0e,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x13,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x64, 0x65,
0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x1a, 0x5c, 0x0a, 0x08, 0x4d, 0x65,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x50, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18,
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6f, 0x62, 0x73,
0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x6c, 0x6f, 0x67, 0x67, 0x69,
0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x70, 0x63, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x63,
0x6f, 0x72, 0x64, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72,
0x79, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x1a, 0x37, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61,
0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x1a, 0xd2, 0x01, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x4d, 0x0a,
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x67, 0x72,
0x70, 0x63, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79,
0x2e, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x70, 0x63,
0x4c, 0x6f, 0x67, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70, 0x5f, 0x70, 0x6f, 0x72,
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x69, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x22,
0x45, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f,
0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50,
0x45, 0x5f, 0x49, 0x50, 0x56, 0x34, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45,
0x5f, 0x49, 0x50, 0x56, 0x36, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f,
0x55, 0x4e, 0x49, 0x58, 0x10, 0x03, 0x22, 0xe5, 0x01, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x54, 0x79, 0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x52, 0x50, 0x43, 0x5f, 0x43, 0x41, 0x4c,
0x4c, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x47,
0x52, 0x50, 0x43, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54,
0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x47, 0x52, 0x50,
0x43, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x5f,
0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x47, 0x52, 0x50, 0x43,
0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x5f, 0x4d, 0x45,
0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x03, 0x12, 0x1e, 0x0a, 0x1a, 0x47, 0x52, 0x50, 0x43, 0x5f,
0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x5f, 0x4d, 0x45,
0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x52, 0x50, 0x43, 0x5f,
0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x54, 0x52, 0x41, 0x49, 0x4c, 0x45, 0x52, 0x10, 0x05, 0x12, 0x18,
0x0a, 0x14, 0x47, 0x52, 0x50, 0x43, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x48, 0x41, 0x4c, 0x46,
0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x10, 0x06, 0x12, 0x14, 0x0a, 0x10, 0x47, 0x52, 0x50, 0x43,
0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x07, 0x22, 0x47,
0x0a, 0x0b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x12, 0x12, 0x0a,
0x0e, 0x4c, 0x4f, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
0x00, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x4f, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45,
0x4e, 0x54, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x4f, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x53,
0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x02, 0x22, 0xa0, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c,
0x65, 0x76, 0x65, 0x6c, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45,
0x4c, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c,
0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x01,
0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x44, 0x45,
0x42, 0x55, 0x47, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56,
0x45, 0x4c, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x4c, 0x4f, 0x47,
0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x04, 0x12, 0x13, 0x0a,
0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52,
0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x4f, 0x47, 0x5f, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f,
0x43, 0x52, 0x49, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x06, 0x42, 0x77, 0x0a, 0x1d, 0x69, 0x6f,
0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c,
0x69, 0x74, 0x79, 0x2e, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x19, 0x4f, 0x62, 0x73,
0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e,
0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63,
0x2f, 0x67, 0x63, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c, 0x69,
0x74, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x6f, 0x67, 0x67,
0x69, 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_gcp_observability_internal_logging_logging_proto_rawDescOnce sync.Once
file_gcp_observability_internal_logging_logging_proto_rawDescData = file_gcp_observability_internal_logging_logging_proto_rawDesc
)
func file_gcp_observability_internal_logging_logging_proto_rawDescGZIP() []byte {
file_gcp_observability_internal_logging_logging_proto_rawDescOnce.Do(func() {
file_gcp_observability_internal_logging_logging_proto_rawDescData = protoimpl.X.CompressGZIP(file_gcp_observability_internal_logging_logging_proto_rawDescData)
})
return file_gcp_observability_internal_logging_logging_proto_rawDescData
}
var file_gcp_observability_internal_logging_logging_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
var file_gcp_observability_internal_logging_logging_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_gcp_observability_internal_logging_logging_proto_goTypes = []interface{}{
(GrpcLogRecord_EventType)(0), // 0: grpc.observability.logging.v1.GrpcLogRecord.EventType
(GrpcLogRecord_EventLogger)(0), // 1: grpc.observability.logging.v1.GrpcLogRecord.EventLogger
(GrpcLogRecord_LogLevel)(0), // 2: grpc.observability.logging.v1.GrpcLogRecord.LogLevel
(GrpcLogRecord_Address_Type)(0), // 3: grpc.observability.logging.v1.GrpcLogRecord.Address.Type
(*GrpcLogRecord)(nil), // 4: grpc.observability.logging.v1.GrpcLogRecord
(*GrpcLogRecord_Metadata)(nil), // 5: grpc.observability.logging.v1.GrpcLogRecord.Metadata
(*GrpcLogRecord_MetadataEntry)(nil), // 6: grpc.observability.logging.v1.GrpcLogRecord.MetadataEntry
(*GrpcLogRecord_Address)(nil), // 7: grpc.observability.logging.v1.GrpcLogRecord.Address
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
(*durationpb.Duration)(nil), // 9: google.protobuf.Duration
}
var file_gcp_observability_internal_logging_logging_proto_depIdxs = []int32{
8, // 0: grpc.observability.logging.v1.GrpcLogRecord.timestamp:type_name -> google.protobuf.Timestamp
0, // 1: grpc.observability.logging.v1.GrpcLogRecord.event_type:type_name -> grpc.observability.logging.v1.GrpcLogRecord.EventType
1, // 2: grpc.observability.logging.v1.GrpcLogRecord.event_logger:type_name -> grpc.observability.logging.v1.GrpcLogRecord.EventLogger
2, // 3: grpc.observability.logging.v1.GrpcLogRecord.log_level:type_name -> grpc.observability.logging.v1.GrpcLogRecord.LogLevel
7, // 4: grpc.observability.logging.v1.GrpcLogRecord.peer_address:type_name -> grpc.observability.logging.v1.GrpcLogRecord.Address
9, // 5: grpc.observability.logging.v1.GrpcLogRecord.timeout:type_name -> google.protobuf.Duration
5, // 6: grpc.observability.logging.v1.GrpcLogRecord.metadata:type_name -> grpc.observability.logging.v1.GrpcLogRecord.Metadata
6, // 7: grpc.observability.logging.v1.GrpcLogRecord.Metadata.entry:type_name -> grpc.observability.logging.v1.GrpcLogRecord.MetadataEntry
3, // 8: grpc.observability.logging.v1.GrpcLogRecord.Address.type:type_name -> grpc.observability.logging.v1.GrpcLogRecord.Address.Type
9, // [9:9] is the sub-list for method output_type
9, // [9:9] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
}
func init() { file_gcp_observability_internal_logging_logging_proto_init() }
func file_gcp_observability_internal_logging_logging_proto_init() {
if File_gcp_observability_internal_logging_logging_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_gcp_observability_internal_logging_logging_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GrpcLogRecord); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_gcp_observability_internal_logging_logging_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GrpcLogRecord_Metadata); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_gcp_observability_internal_logging_logging_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GrpcLogRecord_MetadataEntry); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_gcp_observability_internal_logging_logging_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GrpcLogRecord_Address); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_gcp_observability_internal_logging_logging_proto_rawDesc,
NumEnums: 4,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_gcp_observability_internal_logging_logging_proto_goTypes,
DependencyIndexes: file_gcp_observability_internal_logging_logging_proto_depIdxs,
EnumInfos: file_gcp_observability_internal_logging_logging_proto_enumTypes,
MessageInfos: file_gcp_observability_internal_logging_logging_proto_msgTypes,
}.Build()
File_gcp_observability_internal_logging_logging_proto = out.File
file_gcp_observability_internal_logging_logging_proto_rawDesc = nil
file_gcp_observability_internal_logging_logging_proto_goTypes = nil
file_gcp_observability_internal_logging_logging_proto_depIdxs = nil
}

View File

@ -1,153 +0,0 @@
// Copyright 2022 The gRPC 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.
syntax = "proto3";
package grpc.observability.logging.v1;
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
option java_package = "io.grpc.observability.logging";
option java_multiple_files = true;
option java_outer_classname = "ObservabilityLoggingProto";
option go_package = "google.golang.org/grpc/gcp/observability/internal/logging";
message GrpcLogRecord {
// List of event types
enum EventType {
// Unknown event type
GRPC_CALL_UNKNOWN = 0;
// Header sent from client to server
GRPC_CALL_REQUEST_HEADER = 1;
// Header sent from server to client
GRPC_CALL_RESPONSE_HEADER = 2;
// Message sent from client to server
GRPC_CALL_REQUEST_MESSAGE = 3;
// Message sent from server to client
GRPC_CALL_RESPONSE_MESSAGE = 4;
// Trailer indicates the end of the gRPC call
GRPC_CALL_TRAILER = 5;
// A signal that client is done sending
GRPC_CALL_HALF_CLOSE = 6;
// A signal that the rpc is canceled
GRPC_CALL_CANCEL = 7;
}
// The entity that generates the log entry
enum EventLogger {
LOGGER_UNKNOWN = 0;
LOGGER_CLIENT = 1;
LOGGER_SERVER = 2;
}
// The log severity level of the log entry
enum LogLevel {
LOG_LEVEL_UNKNOWN = 0;
LOG_LEVEL_TRACE = 1;
LOG_LEVEL_DEBUG = 2;
LOG_LEVEL_INFO = 3;
LOG_LEVEL_WARN = 4;
LOG_LEVEL_ERROR = 5;
LOG_LEVEL_CRITICAL = 6;
}
// The timestamp of the log event
google.protobuf.Timestamp timestamp = 1;
// Uniquely identifies a call. The value must not be 0 in order to disambiguate
// from an unset value.
// Each call may have several log entries. They will all have the same rpc_id.
// Nothing is guaranteed about their value other than they are unique across
// different RPCs in the same gRPC process.
string rpc_id = 2;
EventType event_type = 3; // one of the above EventType enum
EventLogger event_logger = 4; // one of the above EventLogger enum
// the name of the service
string service_name = 5;
// the name of the RPC method
string method_name = 6;
LogLevel log_level = 7; // one of the above LogLevel enum
// Peer address information. On client side, peer is logged on server
// header event or trailer event (if trailer-only). On server side, peer
// is always logged on the client header event.
Address peer_address = 8;
// the RPC timeout value
google.protobuf.Duration timeout = 11;
// A single process may be used to run multiple virtual servers with
// different identities.
// The authority is the name of such a server identify. It is typically a
// portion of the URI in the form of <host> or <host>:<port>.
string authority = 12;
// Size of the message or metadata, depending on the event type,
// regardless of whether the full message or metadata is being logged
// (i.e. could be truncated or omitted).
uint32 payload_size = 13;
// true if message or metadata field is either truncated or omitted due
// to config options
bool payload_truncated = 14;
// Used by header event or trailer event
Metadata metadata = 15;
// The entry sequence ID for this call. The first message has a value of 1,
// to disambiguate from an unset value. The purpose of this field is to
// detect missing entries in environments where durability or ordering is
// not guaranteed.
uint64 sequence_id = 16;
// Used by message event
bytes message = 17;
// The gRPC status code
uint32 status_code = 18;
// The gRPC status message
string status_message = 19;
// The value of the grpc-status-details-bin metadata key, if any.
// This is always an encoded google.rpc.Status message
bytes status_details = 20;
// A list of metadata pairs
message Metadata {
repeated MetadataEntry entry = 1;
}
// One metadata key value pair
message MetadataEntry {
string key = 1;
bytes value = 2;
}
// Address information
message Address {
enum Type {
TYPE_UNKNOWN = 0;
TYPE_IPV4 = 1; // in 1.2.3.4 form
TYPE_IPV6 = 2; // IPv6 canonical form (RFC5952 section 4)
TYPE_UNIX = 3; // UDS string
}
Type type = 1;
string address = 2;
// only for TYPE_IPV4 and TYPE_IPV6
uint32 ip_port = 3;
}
}

View File

@ -19,325 +19,441 @@
package observability
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"strings"
"sync/atomic"
"unsafe"
"time"
gcplogging "cloud.google.com/go/logging"
"github.com/google/uuid"
"google.golang.org/grpc"
binlogpb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
grpclogrecordpb "google.golang.org/grpc/gcp/observability/internal/logging"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/binarylog"
iblog "google.golang.org/grpc/internal/binarylog"
"google.golang.org/grpc/internal/grpcutil"
)
var lExporter loggingExporter
var newLoggingExporter = newCloudLoggingExporter
// translateMetadata translates the metadata from Binary Logging format to
// its GrpcLogRecord equivalent.
func translateMetadata(m *binlogpb.Metadata) *grpclogrecordpb.GrpcLogRecord_Metadata {
var res grpclogrecordpb.GrpcLogRecord_Metadata
res.Entry = make([]*grpclogrecordpb.GrpcLogRecord_MetadataEntry, len(m.Entry))
for i, e := range m.Entry {
res.Entry[i] = &grpclogrecordpb.GrpcLogRecord_MetadataEntry{
Key: e.Key,
Value: e.Value,
// its GrpcLogEntry equivalent.
func translateMetadata(m *binlogpb.Metadata) map[string]string {
metadata := make(map[string]string)
for _, entry := range m.GetEntry() {
entryKey := entry.GetKey()
var newVal string
if strings.HasSuffix(entryKey, "-bin") { // bin header
newVal = base64.StdEncoding.EncodeToString(entry.GetValue())
} else { // normal header
newVal = string(entry.GetValue())
}
var oldVal string
var ok bool
if oldVal, ok = metadata[entryKey]; !ok {
metadata[entryKey] = newVal
continue
}
return &res
metadata[entryKey] = oldVal + "," + newVal
}
return metadata
}
func setPeerIfPresent(binlogEntry *binlogpb.GrpcLogEntry, grpcLogRecord *grpclogrecordpb.GrpcLogRecord) {
func setPeerIfPresent(binlogEntry *binlogpb.GrpcLogEntry, grpcLogEntry *grpcLogEntry) {
if binlogEntry.GetPeer() != nil {
grpcLogRecord.PeerAddress = &grpclogrecordpb.GrpcLogRecord_Address{
Type: grpclogrecordpb.GrpcLogRecord_Address_Type(binlogEntry.Peer.Type),
Address: binlogEntry.Peer.Address,
IpPort: binlogEntry.Peer.IpPort,
}
grpcLogEntry.Peer.Type = addrType(binlogEntry.GetPeer().GetType())
grpcLogEntry.Peer.Address = binlogEntry.GetPeer().GetAddress()
grpcLogEntry.Peer.IPPort = binlogEntry.GetPeer().GetIpPort()
}
}
var loggerTypeToEventLogger = map[binlogpb.GrpcLogEntry_Logger]grpclogrecordpb.GrpcLogRecord_EventLogger{
binlogpb.GrpcLogEntry_LOGGER_UNKNOWN: grpclogrecordpb.GrpcLogRecord_LOGGER_UNKNOWN,
binlogpb.GrpcLogEntry_LOGGER_CLIENT: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
binlogpb.GrpcLogEntry_LOGGER_SERVER: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
var loggerTypeToEventLogger = map[binlogpb.GrpcLogEntry_Logger]loggerType{
binlogpb.GrpcLogEntry_LOGGER_UNKNOWN: loggerUnknown,
binlogpb.GrpcLogEntry_LOGGER_CLIENT: loggerClient,
binlogpb.GrpcLogEntry_LOGGER_SERVER: loggerServer,
}
type eventType int
const (
// eventTypeUnknown is an unknown event type.
eventTypeUnknown eventType = iota
// eventTypeClientHeader is a header sent from client to server.
eventTypeClientHeader
// eventTypeServerHeader is a header sent from server to client.
eventTypeServerHeader
// eventTypeClientMessage is a message sent from client to server.
eventTypeClientMessage
// eventTypeServerMessage is a message sent from server to client.
eventTypeServerMessage
// eventTypeClientHalfClose is a signal that the loggerClient is done sending.
eventTypeClientHalfClose
// eventTypeServerTrailer indicated the end of a gRPC call.
eventTypeServerTrailer
// eventTypeCancel is a signal that the rpc is canceled.
eventTypeCancel
)
func (t eventType) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString(`"`)
switch t {
case eventTypeUnknown:
buffer.WriteString("EVENT_TYPE_UNKNOWN")
case eventTypeClientHeader:
buffer.WriteString("CLIENT_HEADER")
case eventTypeServerHeader:
buffer.WriteString("SERVER_HEADER")
case eventTypeClientMessage:
buffer.WriteString("CLIENT_MESSAGE")
case eventTypeServerMessage:
buffer.WriteString("SERVER_MESSAGE")
case eventTypeClientHalfClose:
buffer.WriteString("CLIENT_HALF_CLOSE")
case eventTypeServerTrailer:
buffer.WriteString("SERVER_TRAILER")
case eventTypeCancel:
buffer.WriteString("CANCEL")
}
buffer.WriteString(`"`)
return buffer.Bytes(), nil
}
type loggerType int
const (
loggerUnknown loggerType = iota
loggerClient
loggerServer
)
func (t loggerType) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString(`"`)
switch t {
case loggerUnknown:
buffer.WriteString("LOGGER_UNKNOWN")
case loggerClient:
buffer.WriteString("CLIENT")
case loggerServer:
buffer.WriteString("SERVER")
}
buffer.WriteString(`"`)
return buffer.Bytes(), nil
}
type payload struct {
Metadata map[string]string `json:"metadata,omitempty"`
// Timeout is the RPC timeout value.
Timeout time.Duration `json:"timeout,omitempty"`
// StatusCode is the gRPC status code.
StatusCode uint32 `json:"statusCode,omitempty"`
// StatusMessage is the gRPC status message.
StatusMessage string `json:"statusMessage,omitempty"`
// StatusDetails is the value of the grpc-status-details-bin metadata key,
// if any. This is always an encoded google.rpc.Status message.
StatusDetails []byte `json:"statusDetails,omitempty"`
// MessageLength is the length of the message.
MessageLength uint32 `json:"messageLength,omitempty"`
// Message is the message of this entry. This is populated in the case of a
// message event.
Message []byte `json:"message,omitempty"`
}
type addrType int
const (
typeUnknown addrType = iota // `json:"TYPE_UNKNOWN"`
typeIPv4 // `json:"TYPE_IPV4"`
typeIPv6 // `json:"TYPE_IPV6"`
typeUnix // `json:"TYPE_UNIX"`
)
func (at addrType) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString(`"`)
switch at {
case typeUnknown:
buffer.WriteString("TYPE_UNKNOWN")
case typeIPv4:
buffer.WriteString("TYPE_IPV4")
case typeIPv6:
buffer.WriteString("TYPE_IPV6")
case typeUnix:
buffer.WriteString("TYPE_UNIX")
}
buffer.WriteString(`"`)
return buffer.Bytes(), nil
}
type address struct {
// Type is the address type of the address of the peer of the RPC.
Type addrType `json:"type,omitempty"`
// Address is the address of the peer of the RPC.
Address string `json:"address,omitempty"`
// IPPort is the ip and port in string form. It is used only for addrType
// typeIPv4 and typeIPv6.
IPPort uint32 `json:"ipPort,omitempty"`
}
type grpcLogEntry struct {
// CallID is a uuid which uniquely identifies a call. Each call may have
// several log entries. They will all have the same CallID. Nothing is
// guaranteed about their value other than they are unique across different
// RPCs in the same gRPC process.
CallID string `json:"callId,omitempty"`
// SequenceID is the entry sequence ID for this call. The first message has
// a value of 1, to disambiguate from an unset value. The purpose of this
// field is to detect missing entries in environments where durability or
// ordering is not guaranteed.
SequenceID uint64 `json:"sequenceId,omitempty"`
// Type is the type of binary logging event being logged.
Type eventType `json:"type,omitempty"`
// Logger is the entity that generates the log entry.
Logger loggerType `json:"logger,omitempty"`
// Payload is the payload of this log entry.
Payload payload `json:"payload,omitempty"`
// PayloadTruncated is whether the message or metadata field is either
// truncated or emitted due to options specified in the configuration.
PayloadTruncated bool `json:"payloadTruncated,omitempty"`
// Peer is information about the Peer of the RPC.
Peer address `json:"peer,omitempty"`
// A single process may be used to run multiple virtual servers with
// different identities.
// Authority is the name of such a server identify. It is typically a
// portion of the URI in the form of <host> or <host>:<port>.
Authority string `json:"authority,omitempty"`
// ServiceName is the name of the service.
ServiceName string `json:"serviceName,omitempty"`
// MethodName is the name of the RPC method.
MethodName string `json:"methodName,omitempty"`
}
type methodLoggerBuilder interface {
Build(iblog.LogEntryConfig) *binlogpb.GrpcLogEntry
}
type binaryMethodLogger struct {
rpcID, serviceName, methodName string
originalMethodLogger iblog.MethodLogger
childMethodLogger iblog.MethodLogger
callID, serviceName, methodName, authority string
mlb methodLoggerBuilder
exporter loggingExporter
}
func (ml *binaryMethodLogger) Log(c iblog.LogEntryConfig) {
// Invoke the original MethodLogger to maintain backward compatibility
if ml.originalMethodLogger != nil {
ml.originalMethodLogger.Log(c)
func (bml *binaryMethodLogger) Log(c iblog.LogEntryConfig) {
binLogEntry := bml.mlb.Build(c)
grpcLogEntry := &grpcLogEntry{
CallID: bml.callID,
SequenceID: binLogEntry.GetSequenceIdWithinCall(),
Logger: loggerTypeToEventLogger[binLogEntry.Logger],
}
// Fetch the compiled binary logging log entry
if ml.childMethodLogger == nil {
logger.Info("No wrapped method logger found")
return
}
var binlogEntry *binlogpb.GrpcLogEntry
o, ok := ml.childMethodLogger.(interface {
Build(iblog.LogEntryConfig) *binlogpb.GrpcLogEntry
})
if !ok {
logger.Error("Failed to locate the Build method in wrapped method logger")
return
}
binlogEntry = o.Build(c)
// Translate to GrpcLogRecord
grpcLogRecord := &grpclogrecordpb.GrpcLogRecord{
Timestamp: binlogEntry.GetTimestamp(),
RpcId: ml.rpcID,
SequenceId: binlogEntry.GetSequenceIdWithinCall(),
EventLogger: loggerTypeToEventLogger[binlogEntry.Logger],
// Making DEBUG the default LogLevel
LogLevel: grpclogrecordpb.GrpcLogRecord_LOG_LEVEL_DEBUG,
}
switch binlogEntry.GetType() {
switch binLogEntry.GetType() {
case binlogpb.GrpcLogEntry_EVENT_TYPE_UNKNOWN:
grpcLogRecord.EventType = grpclogrecordpb.GrpcLogRecord_GRPC_CALL_UNKNOWN
grpcLogEntry.Type = eventTypeUnknown
case binlogpb.GrpcLogEntry_EVENT_TYPE_CLIENT_HEADER:
grpcLogRecord.EventType = grpclogrecordpb.GrpcLogRecord_GRPC_CALL_REQUEST_HEADER
if binlogEntry.GetClientHeader() != nil {
methodName := binlogEntry.GetClientHeader().MethodName
grpcLogEntry.Type = eventTypeClientHeader
if binLogEntry.GetClientHeader() != nil {
methodName := binLogEntry.GetClientHeader().MethodName
// Example method name: /grpc.testing.TestService/UnaryCall
if strings.Contains(methodName, "/") {
tokens := strings.Split(methodName, "/")
if len(tokens) == 3 {
// Record service name and method name for all events
ml.serviceName = tokens[1]
ml.methodName = tokens[2]
// Record service name and method name for all events.
bml.serviceName = tokens[1]
bml.methodName = tokens[2]
} else {
logger.Infof("Malformed method name: %v", methodName)
}
}
grpcLogRecord.Timeout = binlogEntry.GetClientHeader().Timeout
grpcLogRecord.Authority = binlogEntry.GetClientHeader().Authority
grpcLogRecord.Metadata = translateMetadata(binlogEntry.GetClientHeader().Metadata)
bml.authority = binLogEntry.GetClientHeader().GetAuthority()
grpcLogEntry.Payload.Timeout = binLogEntry.GetClientHeader().GetTimeout().AsDuration()
grpcLogEntry.Payload.Metadata = translateMetadata(binLogEntry.GetClientHeader().GetMetadata())
}
grpcLogRecord.PayloadTruncated = binlogEntry.GetPayloadTruncated()
setPeerIfPresent(binlogEntry, grpcLogRecord)
grpcLogEntry.PayloadTruncated = binLogEntry.GetPayloadTruncated()
setPeerIfPresent(binLogEntry, grpcLogEntry)
case binlogpb.GrpcLogEntry_EVENT_TYPE_SERVER_HEADER:
grpcLogRecord.EventType = grpclogrecordpb.GrpcLogRecord_GRPC_CALL_RESPONSE_HEADER
grpcLogRecord.Metadata = translateMetadata(binlogEntry.GetServerHeader().Metadata)
grpcLogRecord.PayloadTruncated = binlogEntry.GetPayloadTruncated()
setPeerIfPresent(binlogEntry, grpcLogRecord)
grpcLogEntry.Type = eventTypeServerHeader
if binLogEntry.GetServerHeader() != nil {
grpcLogEntry.Payload.Metadata = translateMetadata(binLogEntry.GetServerHeader().GetMetadata())
}
grpcLogEntry.PayloadTruncated = binLogEntry.GetPayloadTruncated()
setPeerIfPresent(binLogEntry, grpcLogEntry)
case binlogpb.GrpcLogEntry_EVENT_TYPE_CLIENT_MESSAGE:
grpcLogRecord.EventType = grpclogrecordpb.GrpcLogRecord_GRPC_CALL_REQUEST_MESSAGE
grpcLogRecord.Message = binlogEntry.GetMessage().GetData()
grpcLogRecord.PayloadSize = binlogEntry.GetMessage().GetLength()
grpcLogRecord.PayloadTruncated = binlogEntry.GetPayloadTruncated()
grpcLogEntry.Type = eventTypeClientMessage
grpcLogEntry.Payload.Message = binLogEntry.GetMessage().GetData()
grpcLogEntry.Payload.MessageLength = binLogEntry.GetMessage().GetLength()
grpcLogEntry.PayloadTruncated = binLogEntry.GetPayloadTruncated()
case binlogpb.GrpcLogEntry_EVENT_TYPE_SERVER_MESSAGE:
grpcLogRecord.EventType = grpclogrecordpb.GrpcLogRecord_GRPC_CALL_RESPONSE_MESSAGE
grpcLogRecord.Message = binlogEntry.GetMessage().GetData()
grpcLogRecord.PayloadSize = binlogEntry.GetMessage().GetLength()
grpcLogRecord.PayloadTruncated = binlogEntry.GetPayloadTruncated()
grpcLogEntry.Type = eventTypeServerMessage
grpcLogEntry.Payload.Message = binLogEntry.GetMessage().GetData()
grpcLogEntry.Payload.MessageLength = binLogEntry.GetMessage().GetLength()
grpcLogEntry.PayloadTruncated = binLogEntry.GetPayloadTruncated()
case binlogpb.GrpcLogEntry_EVENT_TYPE_CLIENT_HALF_CLOSE:
grpcLogRecord.EventType = grpclogrecordpb.GrpcLogRecord_GRPC_CALL_HALF_CLOSE
grpcLogEntry.Type = eventTypeClientHalfClose
case binlogpb.GrpcLogEntry_EVENT_TYPE_SERVER_TRAILER:
grpcLogRecord.EventType = grpclogrecordpb.GrpcLogRecord_GRPC_CALL_TRAILER
grpcLogRecord.Metadata = translateMetadata(binlogEntry.GetTrailer().Metadata)
grpcLogRecord.StatusCode = binlogEntry.GetTrailer().GetStatusCode()
grpcLogRecord.StatusMessage = binlogEntry.GetTrailer().GetStatusMessage()
grpcLogRecord.StatusDetails = binlogEntry.GetTrailer().GetStatusDetails()
grpcLogRecord.PayloadTruncated = binlogEntry.GetPayloadTruncated()
setPeerIfPresent(binlogEntry, grpcLogRecord)
grpcLogEntry.Type = eventTypeServerTrailer
grpcLogEntry.Payload.Metadata = translateMetadata(binLogEntry.GetTrailer().Metadata)
grpcLogEntry.Payload.StatusCode = binLogEntry.GetTrailer().GetStatusCode()
grpcLogEntry.Payload.StatusMessage = binLogEntry.GetTrailer().GetStatusMessage()
grpcLogEntry.Payload.StatusDetails = binLogEntry.GetTrailer().GetStatusDetails()
grpcLogEntry.PayloadTruncated = binLogEntry.GetPayloadTruncated()
setPeerIfPresent(binLogEntry, grpcLogEntry)
case binlogpb.GrpcLogEntry_EVENT_TYPE_CANCEL:
grpcLogRecord.EventType = grpclogrecordpb.GrpcLogRecord_GRPC_CALL_CANCEL
grpcLogEntry.Type = eventTypeCancel
default:
logger.Infof("Unknown event type: %v", binlogEntry.Type)
logger.Infof("Unknown event type: %v", binLogEntry.Type)
return
}
grpcLogRecord.ServiceName = ml.serviceName
grpcLogRecord.MethodName = ml.methodName
ml.exporter.EmitGrpcLogRecord(grpcLogRecord)
grpcLogEntry.ServiceName = bml.serviceName
grpcLogEntry.MethodName = bml.methodName
grpcLogEntry.Authority = bml.authority
gcploggingEntry := gcplogging.Entry{
Timestamp: binLogEntry.GetTimestamp().AsTime(),
Severity: 100,
Payload: grpcLogEntry,
}
bml.exporter.EmitGcpLoggingEntry(gcploggingEntry)
}
type eventConfig struct {
ServiceMethod map[string]bool
Services map[string]bool
MatchAll bool
// If true, won't log anything.
Exclude bool
HeaderBytes uint64
MessageBytes uint64
}
type binaryLogger struct {
// originalLogger is needed to ensure binary logging users won't be impacted
// by this plugin. Users are allowed to subscribe to a completely different
// set of methods.
originalLogger iblog.Logger
// exporter is a loggingExporter and the handle for uploading collected data
// to backends.
exporter unsafe.Pointer // loggingExporter
// logger is a iblog.Logger wrapped for reusing the pattern matching logic
// and the method logger creating logic.
logger unsafe.Pointer // iblog.Logger
EventConfigs []eventConfig
exporter loggingExporter
}
func (l *binaryLogger) loadExporter() loggingExporter {
ptrPtr := atomic.LoadPointer(&l.exporter)
if ptrPtr == nil {
func (bl *binaryLogger) GetMethodLogger(methodName string) iblog.MethodLogger {
s, _, err := grpcutil.ParseMethod(methodName)
if err != nil {
logger.Infof("binarylogging: failed to parse %q: %v", methodName, err)
return nil
}
exporterPtr := (*loggingExporter)(ptrPtr)
return *exporterPtr
}
func (l *binaryLogger) loadLogger() iblog.Logger {
ptrPtr := atomic.LoadPointer(&l.logger)
if ptrPtr == nil {
for _, eventConfig := range bl.EventConfigs {
if eventConfig.MatchAll || eventConfig.ServiceMethod[methodName] || eventConfig.Services[s] {
if eventConfig.Exclude {
return nil
}
loggerPtr := (*iblog.Logger)(ptrPtr)
return *loggerPtr
}
func (l *binaryLogger) GetMethodLogger(methodName string) iblog.MethodLogger {
var ol iblog.MethodLogger
if l.originalLogger != nil {
ol = l.originalLogger.GetMethodLogger(methodName)
}
// Prevent logging from logging, traces, and metrics API calls.
if strings.HasPrefix(methodName, "/google.logging.v2.LoggingServiceV2/") || strings.HasPrefix(methodName, "/google.monitoring.v3.MetricService/") ||
strings.HasPrefix(methodName, "/google.devtools.cloudtrace.v2.TraceService/") {
return ol
}
// If no exporter is specified, there is no point creating a method
// logger. We don't have any chance to inject exporter after its
// creation.
exporter := l.loadExporter()
if exporter == nil {
return ol
}
// If no logger is specified, e.g., during init period, do nothing.
binLogger := l.loadLogger()
if binLogger == nil {
return ol
}
// If this method is not picked by LoggerConfig, do nothing.
ml := binLogger.GetMethodLogger(methodName)
if ml == nil {
return ol
}
return &binaryMethodLogger{
originalMethodLogger: ol,
childMethodLogger: ml,
rpcID: uuid.NewString(),
exporter: exporter,
exporter: bl.exporter,
mlb: iblog.NewTruncatingMethodLogger(eventConfig.HeaderBytes, eventConfig.MessageBytes),
callID: uuid.NewString(),
}
}
}
return nil
}
func (l *binaryLogger) Close() {
if l == nil {
func registerClientRPCEvents(clientRPCEvents []clientRPCEvents, exporter loggingExporter) {
if len(clientRPCEvents) == 0 {
return
}
ePtr := atomic.LoadPointer(&l.exporter)
if ePtr != nil {
exporter := (*loggingExporter)(ePtr)
if err := (*exporter).Close(); err != nil {
logger.Infof("Failed to close logging exporter: %v", err)
var eventConfigs []eventConfig
for _, clientRPCEvent := range clientRPCEvents {
eventConfig := eventConfig{
Exclude: clientRPCEvent.Exclude,
HeaderBytes: uint64(clientRPCEvent.MaxMetadataBytes),
MessageBytes: uint64(clientRPCEvent.MaxMessageBytes),
}
for _, method := range clientRPCEvent.Methods {
eventConfig.ServiceMethod = make(map[string]bool)
eventConfig.Services = make(map[string]bool)
if method == "*" {
eventConfig.MatchAll = true
continue
}
s, m, err := grpcutil.ParseMethod(method)
if err != nil {
continue
}
if m == "*" {
eventConfig.Services[s] = true
continue
}
eventConfig.ServiceMethod[method] = true
}
eventConfigs = append(eventConfigs, eventConfig)
}
clientSideLogger := &binaryLogger{
EventConfigs: eventConfigs,
exporter: exporter,
}
internal.AddGlobalDialOptions.(func(opt ...grpc.DialOption))(internal.WithBinaryLogger.(func(bl binarylog.Logger) grpc.DialOption)(clientSideLogger))
}
func validateExistingMethodLoggerConfig(existing *iblog.MethodLoggerConfig, filter logFilter) bool {
// In future, we could add more validations. Currently, we only check if the
// new filter configs are different than the existing one, if so, we log a
// warning.
if existing != nil && (existing.Header != uint64(filter.HeaderBytes) || existing.Message != uint64(filter.MessageBytes)) {
logger.Warningf("Ignored log_filter config: %+v", filter)
func registerServerRPCEvents(serverRPCEvents []serverRPCEvents, exporter loggingExporter) {
if len(serverRPCEvents) == 0 {
return
}
return existing == nil
var eventConfigs []eventConfig
for _, serverRPCEvent := range serverRPCEvents {
eventConfig := eventConfig{
Exclude: serverRPCEvent.Exclude,
HeaderBytes: uint64(serverRPCEvent.MaxMetadataBytes),
MessageBytes: uint64(serverRPCEvent.MaxMessageBytes),
}
for _, method := range serverRPCEvent.Methods {
eventConfig.ServiceMethod = make(map[string]bool)
eventConfig.Services = make(map[string]bool)
if method == "*" {
eventConfig.MatchAll = true
continue
}
s, m, err := grpcutil.ParseMethod(method)
if err != nil { // Shouldn't happen, already validated at this point.
continue
}
if m == "*" {
eventConfig.Services[s] = true
continue
}
eventConfig.ServiceMethod[method] = true
}
eventConfigs = append(eventConfigs, eventConfig)
}
serverSideLogger := &binaryLogger{
EventConfigs: eventConfigs,
exporter: exporter,
}
internal.AddGlobalServerOptions.(func(opt ...grpc.ServerOption))(internal.BinaryLogger.(func(bl binarylog.Logger) grpc.ServerOption)(serverSideLogger))
}
func createBinaryLoggerConfig(filters []logFilter) iblog.LoggerConfig {
config := iblog.LoggerConfig{
Services: make(map[string]*iblog.MethodLoggerConfig),
Methods: make(map[string]*iblog.MethodLoggerConfig),
}
// Try matching the filters one by one, pick the first match. The
// correctness of the log filter pattern is ensured by config.go.
for _, filter := range filters {
if filter.Pattern == "*" {
// Match a "*"
if !validateExistingMethodLoggerConfig(config.All, filter) {
continue
}
config.All = &iblog.MethodLoggerConfig{Header: uint64(filter.HeaderBytes), Message: uint64(filter.MessageBytes)}
continue
}
tokens := strings.SplitN(filter.Pattern, "/", 2)
filterService := tokens[0]
filterMethod := tokens[1]
if filterMethod == "*" {
// Handle "p.s/*" case
if !validateExistingMethodLoggerConfig(config.Services[filterService], filter) {
continue
}
config.Services[filterService] = &iblog.MethodLoggerConfig{Header: uint64(filter.HeaderBytes), Message: uint64(filter.MessageBytes)}
continue
}
// Exact match like "p.s/m"
if !validateExistingMethodLoggerConfig(config.Methods[filter.Pattern], filter) {
continue
}
config.Methods[filter.Pattern] = &iblog.MethodLoggerConfig{Header: uint64(filter.HeaderBytes), Message: uint64(filter.MessageBytes)}
}
return config
}
// start is the core logic for setting up the custom binary logging logger, and
// it's also useful for testing.
func (l *binaryLogger) start(config *config, exporter loggingExporter) error {
filters := config.LogFilters
if len(filters) == 0 || exporter == nil {
// Doing nothing is allowed
if exporter != nil {
// The exporter is owned by binaryLogger, so we should close it if
// we are not planning to use it.
exporter.Close()
}
logger.Info("Skipping gRPC Observability logger: no config")
func startLogging(ctx context.Context, config *config) error {
if config == nil || config.CloudLogging == nil {
return nil
}
binLogger := iblog.NewLoggerFromConfig(createBinaryLoggerConfig(filters))
if binLogger != nil {
atomic.StorePointer(&l.logger, unsafe.Pointer(&binLogger))
}
atomic.StorePointer(&l.exporter, unsafe.Pointer(&exporter))
logger.Info("Start gRPC Observability logger")
return nil
}
func (l *binaryLogger) Start(ctx context.Context, config *config) error {
if config == nil || !config.EnableCloudLogging {
return nil
}
if config.DestinationProjectID == "" {
return fmt.Errorf("failed to enable CloudLogging: empty destination_project_id")
}
exporter, err := newCloudLoggingExporter(ctx, config)
var err error
lExporter, err = newLoggingExporter(ctx, config)
if err != nil {
return fmt.Errorf("unable to create CloudLogging exporter: %v", err)
}
l.start(config, exporter)
cl := config.CloudLogging
registerClientRPCEvents(cl.ClientRPCEvents, lExporter)
registerServerRPCEvents(cl.ServerRPCEvents, lExporter)
return nil
}
func newBinaryLogger(iblogger iblog.Logger) *binaryLogger {
return &binaryLogger{
originalLogger: iblogger,
func stopLogging() {
internal.ClearGlobalDialOptions()
internal.ClearGlobalServerOptions()
if lExporter != nil {
// This Close() call handles the flushing of the logging buffer.
lExporter.Close()
}
}
var defaultLogger *binaryLogger
func prepareLogging() {
defaultLogger = newBinaryLogger(iblog.GetLogger())
iblog.SetLogger(defaultLogger)
}

File diff suppressed because it is too large Load Diff

View File

@ -34,10 +34,6 @@ import (
var logger = grpclog.Component("observability")
func init() {
prepareLogging()
}
// Start is the opt-in API for gRPC Observability plugin. This function should
// be invoked in the main function, and before creating any gRPC clients or
// servers, otherwise, they might not be instrumented. At high-level, this
@ -55,7 +51,7 @@ func Start(ctx context.Context) error {
return err
}
if config == nil {
return fmt.Errorf("no ObservabilityConfig found, it can be set via env %s", envObservabilityConfig)
return fmt.Errorf("no ObservabilityConfig found")
}
// Set the project ID if it isn't configured manually.
@ -69,7 +65,7 @@ func Start(ctx context.Context) error {
}
// Logging is controlled by the config at methods level.
return defaultLogger.Start(ctx, config)
return startLogging(ctx, config)
}
// End is the clean-up API for gRPC Observability plugin. It is expected to be
@ -79,6 +75,6 @@ func Start(ctx context.Context) error {
//
// Note: this method should only be invoked once.
func End() {
defaultLogger.Close()
stopLogging()
stopOpenCensus()
}

View File

@ -19,12 +19,11 @@
package observability
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"sync"
"testing"
@ -32,18 +31,12 @@ import (
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
grpclogrecordpb "google.golang.org/grpc/gcp/observability/internal/logging"
iblog "google.golang.org/grpc/internal/binarylog"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/leakcheck"
testgrpc "google.golang.org/grpc/interop/grpc_testing"
testpb "google.golang.org/grpc/interop/grpc_testing"
"google.golang.org/grpc/internal/stubserver"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/grpc/test/grpc_testing"
)
type s struct {
@ -75,275 +68,6 @@ var (
defaultRequestCount = 24
)
type testServer struct {
testgrpc.UnimplementedTestServiceServer
}
func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
if err := grpc.SendHeader(ctx, testHeaderMetadata); err != nil {
return nil, status.Errorf(status.Code(err), "grpc.SendHeader(_, %v) = %v, want <nil>", testHeaderMetadata, err)
}
if err := grpc.SetTrailer(ctx, testTrailerMetadata); err != nil {
return nil, status.Errorf(status.Code(err), "grpc.SetTrailer(_, %v) = %v, want <nil>", testTrailerMetadata, err)
}
if bytes.Equal(in.Payload.Body, testErrorPayload) {
return nil, fmt.Errorf(testErrorMessage)
}
return &testpb.SimpleResponse{Payload: in.Payload}, nil
}
type fakeLoggingExporter struct {
t *testing.T
clientEvents []*grpclogrecordpb.GrpcLogRecord
serverEvents []*grpclogrecordpb.GrpcLogRecord
isClosed bool
mu sync.Mutex
}
func (fle *fakeLoggingExporter) EmitGrpcLogRecord(l *grpclogrecordpb.GrpcLogRecord) {
fle.mu.Lock()
defer fle.mu.Unlock()
switch l.EventLogger {
case grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT:
fle.clientEvents = append(fle.clientEvents, l)
case grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER:
fle.serverEvents = append(fle.serverEvents, l)
default:
fle.t.Fatalf("unexpected event logger: %v", l.EventLogger)
}
eventJSON, _ := protojson.Marshal(l)
fle.t.Logf("fakeLoggingExporter Emit: %s", eventJSON)
}
func (fle *fakeLoggingExporter) Close() error {
fle.isClosed = true
return nil
}
// test is an end-to-end test. It should be created with the newTest
// func, modified as needed, and then started with its startServer method.
// It should be cleaned up with the tearDown method.
type test struct {
t *testing.T
fle *fakeLoggingExporter
testServer testgrpc.TestServiceServer // nil means none
// srv and srvAddr are set once startServer is called.
srv *grpc.Server
srvAddr string
cc *grpc.ClientConn // nil until requested via clientConn
}
func (te *test) tearDown() {
if te.cc != nil {
te.cc.Close()
te.cc = nil
}
te.srv.Stop()
End()
if te.fle != nil && !te.fle.isClosed {
te.t.Fatalf("fakeLoggingExporter not closed!")
}
}
// newTest returns a new test using the provided testing.T and
// environment. It is returned with default values. Tests should
// modify it before calling its startServer and clientConn methods.
func newTest(t *testing.T) *test {
return &test{
t: t,
}
}
// startServer starts a gRPC server listening. Callers should defer a
// call to te.tearDown to clean up.
func (te *test) startServer(ts testgrpc.TestServiceServer) {
te.testServer = ts
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
te.t.Fatalf("Failed to listen: %v", err)
}
var opts []grpc.ServerOption
s := grpc.NewServer(opts...)
te.srv = s
if te.testServer != nil {
testgrpc.RegisterTestServiceServer(s, te.testServer)
}
go s.Serve(lis)
te.srvAddr = lis.Addr().String()
}
func (te *test) clientConn() *grpc.ClientConn {
if te.cc != nil {
return te.cc
}
opts := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
grpc.WithUserAgent("test/0.0.1"),
}
var err error
te.cc, err = grpc.Dial(te.srvAddr, opts...)
if err != nil {
te.t.Fatalf("Dial(%q) = %v", te.srvAddr, err)
}
return te.cc
}
func (te *test) enablePluginWithConfig(config *config) {
// Injects the fake exporter for testing purposes
te.fle = &fakeLoggingExporter{t: te.t}
defaultLogger = newBinaryLogger(nil)
iblog.SetLogger(defaultLogger)
if err := defaultLogger.start(config, te.fle); err != nil {
te.t.Fatalf("Failed to start plugin: %v", err)
}
}
func (te *test) enablePluginWithCaptureAll() {
te.enablePluginWithConfig(&config{
EnableCloudLogging: true,
DestinationProjectID: "fake",
LogFilters: []logFilter{
{
Pattern: "*",
HeaderBytes: infinitySizeBytes,
MessageBytes: infinitySizeBytes,
},
},
})
}
func (te *test) enableOpenCensus() {
defaultMetricsReportingInterval = time.Millisecond * 100
config := &config{
EnableCloudLogging: true,
EnableCloudTrace: true,
EnableCloudMonitoring: true,
GlobalTraceSamplingRate: 1.0,
}
startOpenCensus(config)
}
func checkEventCommon(t *testing.T, seen *grpclogrecordpb.GrpcLogRecord) {
if seen.RpcId == "" {
t.Fatalf("expect non-empty RpcId")
}
if seen.SequenceId == 0 {
t.Fatalf("expect non-zero SequenceId")
}
}
func checkEventRequestHeader(t *testing.T, seen *grpclogrecordpb.GrpcLogRecord, want *grpclogrecordpb.GrpcLogRecord) {
checkEventCommon(t, seen)
if seen.EventType != grpclogrecordpb.GrpcLogRecord_GRPC_CALL_REQUEST_HEADER {
t.Fatalf("got %v, want GrpcLogRecord_GRPC_CALL_REQUEST_HEADER", seen.EventType.String())
}
if seen.EventLogger != want.EventLogger {
t.Fatalf("l.EventLogger = %v, want %v", seen.EventLogger, want.EventLogger)
}
if want.Authority != "" && seen.Authority != want.Authority {
t.Fatalf("l.Authority = %v, want %v", seen.Authority, want.Authority)
}
if want.ServiceName != "" && seen.ServiceName != want.ServiceName {
t.Fatalf("l.ServiceName = %v, want %v", seen.ServiceName, want.ServiceName)
}
if want.MethodName != "" && seen.MethodName != want.MethodName {
t.Fatalf("l.MethodName = %v, want %v", seen.MethodName, want.MethodName)
}
if len(seen.Metadata.Entry) != 1 {
t.Fatalf("unexpected header size: %v != 1", len(seen.Metadata.Entry))
}
if seen.Metadata.Entry[0].Key != "header" {
t.Fatalf("unexpected header: %v", seen.Metadata.Entry[0].Key)
}
if !bytes.Equal(seen.Metadata.Entry[0].Value, []byte(testHeaderMetadata["header"][0])) {
t.Fatalf("unexpected header value: %v", seen.Metadata.Entry[0].Value)
}
}
func checkEventResponseHeader(t *testing.T, seen *grpclogrecordpb.GrpcLogRecord, want *grpclogrecordpb.GrpcLogRecord) {
checkEventCommon(t, seen)
if seen.EventType != grpclogrecordpb.GrpcLogRecord_GRPC_CALL_RESPONSE_HEADER {
t.Fatalf("got %v, want GrpcLogRecord_GRPC_CALL_RESPONSE_HEADER", seen.EventType.String())
}
if seen.EventLogger != want.EventLogger {
t.Fatalf("l.EventLogger = %v, want %v", seen.EventLogger, want.EventLogger)
}
if len(seen.Metadata.Entry) != 1 {
t.Fatalf("unexpected header size: %v != 1", len(seen.Metadata.Entry))
}
if seen.Metadata.Entry[0].Key != "header" {
t.Fatalf("unexpected header: %v", seen.Metadata.Entry[0].Key)
}
if !bytes.Equal(seen.Metadata.Entry[0].Value, []byte(testHeaderMetadata["header"][0])) {
t.Fatalf("unexpected header value: %v", seen.Metadata.Entry[0].Value)
}
}
func checkEventRequestMessage(t *testing.T, seen *grpclogrecordpb.GrpcLogRecord, want *grpclogrecordpb.GrpcLogRecord, payload []byte) {
checkEventCommon(t, seen)
if seen.EventType != grpclogrecordpb.GrpcLogRecord_GRPC_CALL_REQUEST_MESSAGE {
t.Fatalf("got %v, want GrpcLogRecord_GRPC_CALL_REQUEST_MESSAGE", seen.EventType.String())
}
if seen.EventLogger != want.EventLogger {
t.Fatalf("l.EventLogger = %v, want %v", seen.EventLogger, want.EventLogger)
}
msg := &testpb.SimpleRequest{Payload: &testpb.Payload{Body: payload}}
msgBytes, _ := proto.Marshal(msg)
if !bytes.Equal(seen.Message, msgBytes) {
t.Fatalf("unexpected payload: %v != %v", seen.Message, payload)
}
}
func checkEventResponseMessage(t *testing.T, seen *grpclogrecordpb.GrpcLogRecord, want *grpclogrecordpb.GrpcLogRecord, payload []byte) {
checkEventCommon(t, seen)
if seen.EventType != grpclogrecordpb.GrpcLogRecord_GRPC_CALL_RESPONSE_MESSAGE {
t.Fatalf("got %v, want GrpcLogRecord_GRPC_CALL_RESPONSE_MESSAGE", seen.EventType.String())
}
if seen.EventLogger != want.EventLogger {
t.Fatalf("l.EventLogger = %v, want %v", seen.EventLogger, want.EventLogger)
}
msg := &testpb.SimpleResponse{Payload: &testpb.Payload{Body: payload}}
msgBytes, _ := proto.Marshal(msg)
if !bytes.Equal(seen.Message, msgBytes) {
t.Fatalf("unexpected payload: %v != %v", seen.Message, payload)
}
}
func checkEventTrailer(t *testing.T, seen *grpclogrecordpb.GrpcLogRecord, want *grpclogrecordpb.GrpcLogRecord) {
checkEventCommon(t, seen)
if seen.EventType != grpclogrecordpb.GrpcLogRecord_GRPC_CALL_TRAILER {
t.Fatalf("got %v, want GrpcLogRecord_GRPC_CALL_TRAILER", seen.EventType.String())
}
if seen.EventLogger != want.EventLogger {
t.Fatalf("l.EventLogger = %v, want %v", seen.EventLogger, want.EventLogger)
}
if seen.StatusCode != want.StatusCode {
t.Fatalf("l.StatusCode = %v, want %v", seen.StatusCode, want.StatusCode)
}
if seen.StatusMessage != want.StatusMessage {
t.Fatalf("l.StatusMessage = %v, want %v", seen.StatusMessage, want.StatusMessage)
}
if !bytes.Equal(seen.StatusDetails, want.StatusDetails) {
t.Fatalf("l.StatusDetails = %v, want %v", seen.StatusDetails, want.StatusDetails)
}
if len(seen.Metadata.Entry) != 1 {
t.Fatalf("unexpected trailer size: %v != 1", len(seen.Metadata.Entry))
}
if seen.Metadata.Entry[0].Key != "trailer" {
t.Fatalf("unexpected trailer: %v", seen.Metadata.Entry[0].Key)
}
if !bytes.Equal(seen.Metadata.Entry[0].Value, []byte(testTrailerMetadata["trailer"][0])) {
t.Fatalf("unexpected trailer value: %v", seen.Metadata.Entry[0].Value)
}
}
const (
TypeOpenCensusViewDistribution string = "distribution"
TypeOpenCensusViewCount = "count"
@ -392,296 +116,67 @@ func (fe *fakeOpenCensusExporter) Close() error {
return nil
}
func (s) TestLoggingForOkCall(t *testing.T) {
te := newTest(t)
defer te.tearDown()
te.enablePluginWithCaptureAll()
te.startServer(&testServer{})
tc := testgrpc.NewTestServiceClient(te.clientConn())
var (
resp *testpb.SimpleResponse
req *testpb.SimpleRequest
err error
)
req = &testpb.SimpleRequest{Payload: &testpb.Payload{Body: testOkPayload}}
tCtx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
resp, err = tc.UnaryCall(metadata.NewOutgoingContext(tCtx, testHeaderMetadata), req)
if err != nil {
t.Fatalf("unary call failed: %v", err)
}
t.Logf("unary call passed: %v", resp)
// Wait for the gRPC transport to gracefully close to ensure no lost event.
te.cc.Close()
te.srv.GracefulStop()
// Check size of events
if len(te.fle.clientEvents) != 5 {
t.Fatalf("expects 5 client events, got %d", len(te.fle.clientEvents))
}
if len(te.fle.serverEvents) != 5 {
t.Fatalf("expects 5 server events, got %d", len(te.fle.serverEvents))
}
// Client events
checkEventRequestHeader(te.t, te.fle.clientEvents[0], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
Authority: te.srvAddr,
ServiceName: "grpc.testing.TestService",
MethodName: "UnaryCall",
})
checkEventRequestMessage(te.t, te.fle.clientEvents[1], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
}, testOkPayload)
checkEventResponseHeader(te.t, te.fle.clientEvents[2], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
})
checkEventResponseMessage(te.t, te.fle.clientEvents[3], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
}, testOkPayload)
checkEventTrailer(te.t, te.fle.clientEvents[4], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
StatusCode: 0,
})
// Server events
checkEventRequestHeader(te.t, te.fle.serverEvents[0], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
})
checkEventRequestMessage(te.t, te.fle.serverEvents[1], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
}, testOkPayload)
checkEventResponseHeader(te.t, te.fle.serverEvents[2], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
})
checkEventResponseMessage(te.t, te.fle.serverEvents[3], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
}, testOkPayload)
checkEventTrailer(te.t, te.fle.serverEvents[4], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
StatusCode: 0,
})
}
func (s) TestLoggingForErrorCall(t *testing.T) {
te := newTest(t)
defer te.tearDown()
te.enablePluginWithCaptureAll()
te.startServer(&testServer{})
tc := testgrpc.NewTestServiceClient(te.clientConn())
req := &testpb.SimpleRequest{Payload: &testpb.Payload{Body: testErrorPayload}}
tCtx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
_, err := tc.UnaryCall(metadata.NewOutgoingContext(tCtx, testHeaderMetadata), req)
if err == nil {
t.Fatalf("unary call expected to fail, but passed")
}
// Wait for the gRPC transport to gracefully close to ensure no lost event.
te.cc.Close()
te.srv.GracefulStop()
// Check size of events
if len(te.fle.clientEvents) != 4 {
t.Fatalf("expects 4 client events, got %d", len(te.fle.clientEvents))
}
if len(te.fle.serverEvents) != 4 {
t.Fatalf("expects 4 server events, got %d", len(te.fle.serverEvents))
}
// Client events
checkEventRequestHeader(te.t, te.fle.clientEvents[0], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
Authority: te.srvAddr,
ServiceName: "grpc.testing.TestService",
MethodName: "UnaryCall",
})
checkEventRequestMessage(te.t, te.fle.clientEvents[1], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
}, testErrorPayload)
checkEventResponseHeader(te.t, te.fle.clientEvents[2], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
})
checkEventTrailer(te.t, te.fle.clientEvents[3], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
StatusCode: 2,
StatusMessage: testErrorMessage,
})
// Server events
checkEventRequestHeader(te.t, te.fle.serverEvents[0], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
})
checkEventRequestMessage(te.t, te.fle.serverEvents[1], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
}, testErrorPayload)
checkEventResponseHeader(te.t, te.fle.serverEvents[2], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
})
checkEventTrailer(te.t, te.fle.serverEvents[3], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
StatusCode: 2,
StatusMessage: testErrorMessage,
})
}
func (s) TestEmptyConfig(t *testing.T) {
te := newTest(t)
defer te.tearDown()
te.enablePluginWithConfig(&config{})
te.startServer(&testServer{})
tc := testgrpc.NewTestServiceClient(te.clientConn())
req := &testpb.SimpleRequest{Payload: &testpb.Payload{Body: testOkPayload}}
tCtx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
resp, err := tc.UnaryCall(metadata.NewOutgoingContext(tCtx, testHeaderMetadata), req)
if err != nil {
t.Fatalf("unary call failed: %v", err)
}
t.Logf("unary call passed: %v", resp)
// Wait for the gRPC transport to gracefully close to ensure no lost event.
te.cc.Close()
te.srv.GracefulStop()
// Check size of events
if len(te.fle.clientEvents) != 0 {
t.Fatalf("expects 0 client events, got %d", len(te.fle.clientEvents))
}
if len(te.fle.serverEvents) != 0 {
t.Fatalf("expects 0 server events, got %d", len(te.fle.serverEvents))
}
}
func (s) TestOverrideConfig(t *testing.T) {
te := newTest(t)
defer te.tearDown()
// Setting 3 filters, expected to use the third filter, because it's the
// most specific one. The third filter allows message payload logging, and
// others disabling the message payload logging. We should observe this
// behavior latter.
te.enablePluginWithConfig(&config{
EnableCloudLogging: true,
DestinationProjectID: "fake",
LogFilters: []logFilter{
{
Pattern: "wont/match",
MessageBytes: 0,
},
{
Pattern: "*",
MessageBytes: 0,
},
{
Pattern: "grpc.testing.TestService/*",
MessageBytes: 4096,
},
},
})
te.startServer(&testServer{})
tc := testgrpc.NewTestServiceClient(te.clientConn())
var (
resp *testpb.SimpleResponse
req *testpb.SimpleRequest
err error
)
req = &testpb.SimpleRequest{Payload: &testpb.Payload{Body: testOkPayload}}
tCtx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
resp, err = tc.UnaryCall(metadata.NewOutgoingContext(tCtx, testHeaderMetadata), req)
if err != nil {
t.Fatalf("unary call failed: %v", err)
}
t.Logf("unary call passed: %v", resp)
// Wait for the gRPC transport to gracefully close to ensure no lost event.
te.cc.Close()
te.srv.GracefulStop()
// Check size of events
if len(te.fle.clientEvents) != 5 {
t.Fatalf("expects 5 client events, got %d", len(te.fle.clientEvents))
}
if len(te.fle.serverEvents) != 5 {
t.Fatalf("expects 5 server events, got %d", len(te.fle.serverEvents))
}
// Check Client message payloads
checkEventRequestMessage(te.t, te.fle.clientEvents[1], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
}, testOkPayload)
checkEventResponseMessage(te.t, te.fle.clientEvents[3], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_CLIENT,
}, testOkPayload)
// Check Server message payloads
checkEventRequestMessage(te.t, te.fle.serverEvents[1], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
}, testOkPayload)
checkEventResponseMessage(te.t, te.fle.serverEvents[3], &grpclogrecordpb.GrpcLogRecord{
EventLogger: grpclogrecordpb.GrpcLogRecord_LOGGER_SERVER,
}, testOkPayload)
}
func (s) TestNoMatch(t *testing.T) {
te := newTest(t)
defer te.tearDown()
// Setting 3 filters, expected to use the second filter. The second filter
// allows message payload logging, and others disabling the message payload
// logging. We should observe this behavior latter.
te.enablePluginWithConfig(&config{
EnableCloudLogging: true,
DestinationProjectID: "fake",
LogFilters: []logFilter{
{
Pattern: "wont/match",
MessageBytes: 0,
},
},
})
te.startServer(&testServer{})
tc := testgrpc.NewTestServiceClient(te.clientConn())
var (
resp *testpb.SimpleResponse
req *testpb.SimpleRequest
err error
)
req = &testpb.SimpleRequest{Payload: &testpb.Payload{Body: testOkPayload}}
tCtx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
resp, err = tc.UnaryCall(metadata.NewOutgoingContext(tCtx, testHeaderMetadata), req)
if err != nil {
t.Fatalf("unary call failed: %v", err)
}
t.Logf("unary call passed: %v", resp)
// Wait for the gRPC transport to gracefully close to ensure no lost event.
te.cc.Close()
te.srv.GracefulStop()
// Check size of events
if len(te.fle.clientEvents) != 0 {
t.Fatalf("expects 0 client events, got %d", len(te.fle.clientEvents))
}
if len(te.fle.serverEvents) != 0 {
t.Fatalf("expects 0 server events, got %d", len(te.fle.serverEvents))
}
}
func (s) TestRefuseStartWithInvalidPatterns(t *testing.T) {
config := &config{
EnableCloudLogging: true,
DestinationProjectID: "fake",
LogFilters: []logFilter{
invalidConfig := &config{
ProjectID: "fake",
CloudLogging: &cloudLogging{
ClientRPCEvents: []clientRPCEvents{
{
Pattern: ":-)",
Methods: []string{":-)"},
MaxMetadataBytes: 30,
MaxMessageBytes: 30,
},
{
Pattern: "*",
},
},
}
configJSON, err := json.Marshal(config)
invalidConfigJSON, err := json.Marshal(invalidConfig)
if err != nil {
t.Fatalf("failed to convert config to JSON: %v", err)
}
os.Setenv(envObservabilityConfigJSON, "")
os.Setenv(envObservabilityConfig, string(configJSON))
oldObservabilityConfig := envconfig.ObservabilityConfig
oldObservabilityConfigFile := envconfig.ObservabilityConfigFile
envconfig.ObservabilityConfig = string(invalidConfigJSON)
envconfig.ObservabilityConfigFile = ""
defer func() {
envconfig.ObservabilityConfig = oldObservabilityConfig
envconfig.ObservabilityConfigFile = oldObservabilityConfigFile
}()
// If there is at least one invalid pattern, which should not be silently tolerated.
if err := Start(context.Background()); err == nil {
t.Fatalf("Invalid patterns not triggering error")
}
}
// TestRefuseStartWithExcludeAndWildCardAll tests the sceanrio where an
// observability configuration is provided with client RPC event specifying to
// exclude, and which matches on the '*' wildcard (any). This should cause an
// error when trying to start the observability system.
func (s) TestRefuseStartWithExcludeAndWildCardAll(t *testing.T) {
invalidConfig := &config{
ProjectID: "fake",
CloudLogging: &cloudLogging{
ClientRPCEvents: []clientRPCEvents{
{
Methods: []string{"*"},
Exclude: true,
MaxMetadataBytes: 30,
MaxMessageBytes: 30,
},
},
},
}
invalidConfigJSON, err := json.Marshal(invalidConfig)
if err != nil {
t.Fatalf("failed to convert config to JSON: %v", err)
}
oldObservabilityConfig := envconfig.ObservabilityConfig
oldObservabilityConfigFile := envconfig.ObservabilityConfigFile
envconfig.ObservabilityConfig = string(invalidConfigJSON)
envconfig.ObservabilityConfigFile = ""
defer func() {
envconfig.ObservabilityConfig = oldObservabilityConfig
envconfig.ObservabilityConfigFile = oldObservabilityConfigFile
}()
// If there is at least one invalid pattern, which should not be silently tolerated.
if err := Start(context.Background()); err == nil {
t.Fatalf("Invalid patterns not triggering error")
@ -701,10 +196,11 @@ func createTmpConfigInFileSystem(rawJSON string) (func(), error) {
if err != nil {
return nil, fmt.Errorf("cannot write marshalled JSON: %v", err)
}
os.Setenv(envObservabilityConfigJSON, configJSONFile.Name())
oldObservabilityConfigFile := envconfig.ObservabilityConfigFile
envconfig.ObservabilityConfigFile = configJSONFile.Name()
return func() {
configJSONFile.Close()
os.Setenv(envObservabilityConfigJSON, "")
envconfig.ObservabilityConfigFile = oldObservabilityConfigFile
}, nil
}
@ -713,8 +209,7 @@ func createTmpConfigInFileSystem(rawJSON string) (func(), error) {
// file path pointing to a JSON encoded config.
func (s) TestJSONEnvVarSet(t *testing.T) {
configJSON := `{
"destination_project_id": "fake",
"log_filters":[{"pattern":"*","header_bytes":1073741824,"message_bytes":1073741824}]
"project_id": "fake"
}`
cleanup, err := createTmpConfigInFileSystem(configJSON)
defer cleanup()
@ -736,24 +231,37 @@ func (s) TestJSONEnvVarSet(t *testing.T) {
// configuration being invalid, even if the direct configuration environment
// variable is set and valid.
func (s) TestBothConfigEnvVarsSet(t *testing.T) {
configJSON := `{
"destination_project_id":"fake",
"log_filters":[{"pattern":":-)"}, {"pattern_string":"*"}]
}`
cleanup, err := createTmpConfigInFileSystem(configJSON)
invalidConfig := &config{
ProjectID: "fake",
CloudLogging: &cloudLogging{
ClientRPCEvents: []clientRPCEvents{
{
Methods: []string{":-)"},
MaxMetadataBytes: 30,
MaxMessageBytes: 30,
},
},
},
}
invalidConfigJSON, err := json.Marshal(invalidConfig)
if err != nil {
t.Fatalf("failed to convert config to JSON: %v", err)
}
cleanup, err := createTmpConfigInFileSystem(string(invalidConfigJSON))
defer cleanup()
if err != nil {
t.Fatalf("failed to create config in file system: %v", err)
}
// This configuration should be ignored, as precedence 2.
validConfig := &config{
EnableCloudLogging: true,
DestinationProjectID: "fake",
LogFilters: []logFilter{
ProjectID: "fake",
CloudLogging: &cloudLogging{
ClientRPCEvents: []clientRPCEvents{
{
Pattern: "*",
HeaderBytes: infinitySizeBytes,
MessageBytes: infinitySizeBytes,
Methods: []string{"*"},
MaxMetadataBytes: 30,
MaxMessageBytes: 30,
},
},
},
}
@ -761,7 +269,11 @@ func (s) TestBothConfigEnvVarsSet(t *testing.T) {
if err != nil {
t.Fatalf("failed to convert config to JSON: %v", err)
}
os.Setenv(envObservabilityConfig, string(validConfigJSON))
oldObservabilityConfig := envconfig.ObservabilityConfig
envconfig.ObservabilityConfig = string(validConfigJSON)
defer func() {
envconfig.ObservabilityConfig = oldObservabilityConfig
}()
if err := Start(context.Background()); err == nil {
t.Fatalf("Invalid patterns not triggering error")
}
@ -772,16 +284,25 @@ func (s) TestBothConfigEnvVarsSet(t *testing.T) {
// location in the file system for configuration, and this location doesn't have
// a file (or valid configuration).
func (s) TestErrInFileSystemEnvVar(t *testing.T) {
os.Setenv(envObservabilityConfigJSON, "/this-file/does-not-exist")
defer os.Setenv(envObservabilityConfigJSON, "")
oldObservabilityConfigFile := envconfig.ObservabilityConfigFile
envconfig.ObservabilityConfigFile = "/this-file/does-not-exist"
defer func() {
envconfig.ObservabilityConfigFile = oldObservabilityConfigFile
}()
if err := Start(context.Background()); err == nil {
t.Fatalf("Invalid file system path not triggering error")
}
}
func (s) TestNoEnvSet(t *testing.T) {
os.Setenv(envObservabilityConfigJSON, "")
os.Setenv(envObservabilityConfig, "")
oldObservabilityConfig := envconfig.ObservabilityConfig
oldObservabilityConfigFile := envconfig.ObservabilityConfigFile
envconfig.ObservabilityConfig = ""
envconfig.ObservabilityConfigFile = ""
defer func() {
envconfig.ObservabilityConfig = oldObservabilityConfig
envconfig.ObservabilityConfigFile = oldObservabilityConfigFile
}()
// If there is no observability config set at all, the Start should return an error.
if err := Start(context.Background()); err == nil {
t.Fatalf("Invalid patterns not triggering error")
@ -789,9 +310,8 @@ func (s) TestNoEnvSet(t *testing.T) {
}
func (s) TestOpenCensusIntegration(t *testing.T) {
te := newTest(t)
defer te.tearDown()
fe := &fakeOpenCensusExporter{SeenViews: make(map[string]string), t: te.t}
defaultMetricsReportingInterval = time.Millisecond * 100
fe := &fakeOpenCensusExporter{SeenViews: make(map[string]string), t: t}
defer func(ne func(config *config) (tracingMetricsExporter, error)) {
newExporter = ne
@ -801,28 +321,58 @@ func (s) TestOpenCensusIntegration(t *testing.T) {
return fe, nil
}
te.enableOpenCensus()
te.startServer(&testServer{})
tc := testgrpc.NewTestServiceClient(te.clientConn())
openCensusOnConfig := &config{
ProjectID: "fake",
CloudMonitoring: &cloudMonitoring{},
CloudTrace: &cloudTrace{
SamplingRate: 1.0,
},
}
cleanup, err := setupObservabilitySystemWithConfig(openCensusOnConfig)
if err != nil {
t.Fatalf("error setting up observability %v", err)
}
defer cleanup()
ss := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, in *grpc_testing.SimpleRequest) (*grpc_testing.SimpleResponse, error) {
return &grpc_testing.SimpleResponse{}, nil
},
FullDuplexCallF: func(stream grpc_testing.TestService_FullDuplexCallServer) error {
for {
_, err := stream.Recv()
if err == io.EOF {
return nil
}
}
},
}
if err := ss.Start(nil); err != nil {
t.Fatalf("Error starting endpoint server: %v", err)
}
defer ss.Stop()
for i := 0; i < defaultRequestCount; i++ {
req := &testpb.SimpleRequest{Payload: &testpb.Payload{Body: testOkPayload}}
tCtx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
_, err := tc.UnaryCall(metadata.NewOutgoingContext(tCtx, testHeaderMetadata), req)
if err != nil {
t.Fatalf("unary call failed: %v", err)
if _, err := ss.Client.UnaryCall(ctx, &grpc_testing.SimpleRequest{Payload: &grpc_testing.Payload{Body: testOkPayload}}); err != nil {
t.Fatalf("Unexpected error from UnaryCall: %v", err)
}
}
t.Logf("unary call passed count=%v", defaultRequestCount)
// Wait for the gRPC transport to gracefully close to ensure no lost event.
te.cc.Close()
te.srv.GracefulStop()
var errs []error
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
stream, err := ss.Client.FullDuplexCall(ctx)
if err != nil {
t.Fatalf("ss.Client.FullDuplexCall failed: %f", err)
}
stream.CloseSend()
if _, err = stream.Recv(); err != io.EOF {
t.Fatalf("unexpected error: %v, expected an EOF error", err)
}
var errs []error
for ctx.Err() == nil {
errs = nil
fe.mu.RLock()
@ -855,7 +405,7 @@ func (s) TestCustomTagsTracingMetrics(t *testing.T) {
}(newExporter)
fe := &fakeOpenCensusExporter{SeenViews: make(map[string]string), t: t}
newExporter = func(config *config) (tracingMetricsExporter, error) {
ct := config.CustomTags
ct := config.Labels
if len(ct) < 1 {
t.Fatalf("less than 2 custom tags sent in")
}
@ -871,12 +421,12 @@ func (s) TestCustomTagsTracingMetrics(t *testing.T) {
// This configuration present in file system and it's defined custom tags should make it
// to the created exporter.
configJSON := `{
"destination_project_id": "fake",
"enable_cloud_trace": true,
"enable_cloud_monitoring": true,
"global_trace_sampling_rate": 1.0,
"custom_tags":{"customtag1":"wow","customtag2":"nice"}
"project_id": "fake",
"cloud_trace": {},
"cloud_monitoring": {"sampling_rate": 1.0},
"labels":{"customtag1":"wow","customtag2":"nice"}
}`
cleanup, err := createTmpConfigInFileSystem(configJSON)
defer cleanup()
@ -888,3 +438,37 @@ func (s) TestCustomTagsTracingMetrics(t *testing.T) {
t.Fatalf("Start() failed with err: %v", err)
}
}
// TestStartErrorsThenEnd tests that an End call after Start errors works
// without problems, as this is a possible codepath in the public observability
// API.
func (s) TestStartErrorsThenEnd(t *testing.T) {
invalidConfig := &config{
ProjectID: "fake",
CloudLogging: &cloudLogging{
ClientRPCEvents: []clientRPCEvents{
{
Methods: []string{":-)"},
MaxMetadataBytes: 30,
MaxMessageBytes: 30,
},
},
},
}
invalidConfigJSON, err := json.Marshal(invalidConfig)
if err != nil {
t.Fatalf("failed to convert config to JSON: %v", err)
}
oldObservabilityConfig := envconfig.ObservabilityConfig
oldObservabilityConfigFile := envconfig.ObservabilityConfigFile
envconfig.ObservabilityConfig = string(invalidConfigJSON)
envconfig.ObservabilityConfigFile = ""
defer func() {
envconfig.ObservabilityConfig = oldObservabilityConfig
envconfig.ObservabilityConfigFile = oldObservabilityConfigFile
}()
if err := Start(context.Background()); err == nil {
t.Fatalf("Invalid patterns not triggering error")
}
End()
}

View File

@ -37,17 +37,17 @@ var (
defaultMetricsReportingInterval = time.Second * 30
)
func tagsToMonitoringLabels(tags map[string]string) *stackdriver.Labels {
labels := &stackdriver.Labels{}
for k, v := range tags {
labels.Set(k, v, "")
func labelsToMonitoringLabels(labels map[string]string) *stackdriver.Labels {
sdLabels := &stackdriver.Labels{}
for k, v := range labels {
sdLabels.Set(k, v, "")
}
return labels
return sdLabels
}
func tagsToTraceAttributes(tags map[string]string) map[string]interface{} {
ta := make(map[string]interface{}, len(tags))
for k, v := range tags {
func labelsToTraceAttributes(labels map[string]string) map[string]interface{} {
ta := make(map[string]interface{}, len(labels))
for k, v := range labels {
ta[k] = v
}
return ta
@ -62,7 +62,6 @@ type tracingMetricsExporter interface {
var exporter tracingMetricsExporter
// global to stub out in tests
var newExporter = newStackdriverExporter
func newStackdriverExporter(config *config) (tracingMetricsExporter, error) {
@ -71,10 +70,10 @@ func newStackdriverExporter(config *config) (tracingMetricsExporter, error) {
logger.Infof("Detected MonitoredResource:: %+v", mr)
var err error
exporter, err := stackdriver.NewExporter(stackdriver.Options{
ProjectID: config.DestinationProjectID,
ProjectID: config.ProjectID,
MonitoredResource: mr,
DefaultMonitoringLabels: tagsToMonitoringLabels(config.CustomTags),
DefaultTraceAttributes: tagsToTraceAttributes(config.CustomTags),
DefaultMonitoringLabels: labelsToMonitoringLabels(config.Labels),
DefaultTraceAttributes: labelsToTraceAttributes(config.Labels),
})
if err != nil {
return nil, fmt.Errorf("failed to create Stackdriver exporter: %v", err)
@ -87,7 +86,7 @@ func newStackdriverExporter(config *config) (tracingMetricsExporter, error) {
func startOpenCensus(config *config) error {
// If both tracing and metrics are disabled, there's no point inject default
// StatsHandler.
if config == nil || (!config.EnableCloudTrace && !config.EnableCloudMonitoring) {
if config == nil || (config.CloudTrace == nil && config.CloudMonitoring == nil) {
return nil
}
@ -98,17 +97,17 @@ func startOpenCensus(config *config) error {
}
var so trace.StartOptions
if config.EnableCloudTrace {
so.Sampler = trace.ProbabilitySampler(config.GlobalTraceSamplingRate)
if config.CloudTrace != nil {
so.Sampler = trace.ProbabilitySampler(config.CloudTrace.SamplingRate)
trace.RegisterExporter(exporter.(trace.Exporter))
logger.Infof("Start collecting and exporting trace spans with global_trace_sampling_rate=%.2f", config.GlobalTraceSamplingRate)
logger.Infof("Start collecting and exporting trace spans with global_trace_sampling_rate=%.2f", config.CloudTrace.SamplingRate)
}
if config.EnableCloudMonitoring {
if err := view.Register(ocgrpc.DefaultClientViews...); err != nil {
if config.CloudMonitoring != nil {
if err := view.Register(ocgrpc.ClientCompletedRPCsView); err != nil {
return fmt.Errorf("failed to register default client views: %v", err)
}
if err := view.Register(ocgrpc.DefaultServerViews...); err != nil {
if err := view.Register(ocgrpc.ServerCompletedRPCsView); err != nil {
return fmt.Errorf("failed to register default server views: %v", err)
}
view.SetReportingPeriod(defaultMetricsReportingInterval)
@ -130,7 +129,6 @@ func stopOpenCensus() {
if exporter != nil {
internal.ClearGlobalDialOptions()
internal.ClearGlobalServerOptions()
// Call these unconditionally, doesn't matter if not registered, will be
// a noop if not registered.
trace.UnregisterExporter(exporter)

View File

@ -37,7 +37,7 @@ type Logger interface {
// binLogger is the global binary logger for the binary. One of this should be
// built at init time from the configuration (environment variable or flags).
//
// It is used to get a methodLogger for each individual method.
// It is used to get a MethodLogger for each individual method.
var binLogger Logger
var grpclogLogger = grpclog.Component("binarylog")
@ -56,11 +56,11 @@ func GetLogger() Logger {
return binLogger
}
// GetMethodLogger returns the methodLogger for the given methodName.
// GetMethodLogger returns the MethodLogger for the given methodName.
//
// methodName should be in the format of "/service/method".
//
// Each methodLogger returned by this method is a new instance. This is to
// Each MethodLogger returned by this method is a new instance. This is to
// generate sequence id within the call.
func GetMethodLogger(methodName string) MethodLogger {
if binLogger == nil {
@ -117,7 +117,7 @@ func (l *logger) setDefaultMethodLogger(ml *MethodLoggerConfig) error {
// Set method logger for "service/*".
//
// New methodLogger with same service overrides the old one.
// New MethodLogger with same service overrides the old one.
func (l *logger) setServiceMethodLogger(service string, ml *MethodLoggerConfig) error {
if _, ok := l.config.Services[service]; ok {
return fmt.Errorf("conflicting service rules for service %v found", service)
@ -131,7 +131,7 @@ func (l *logger) setServiceMethodLogger(service string, ml *MethodLoggerConfig)
// Set method logger for "service/method".
//
// New methodLogger with same method overrides the old one.
// New MethodLogger with same method overrides the old one.
func (l *logger) setMethodMethodLogger(method string, ml *MethodLoggerConfig) error {
if _, ok := l.config.Blacklist[method]; ok {
return fmt.Errorf("conflicting blacklist rules for method %v found", method)
@ -161,11 +161,11 @@ func (l *logger) setBlacklist(method string) error {
return nil
}
// getMethodLogger returns the methodLogger for the given methodName.
// getMethodLogger returns the MethodLogger for the given methodName.
//
// methodName should be in the format of "/service/method".
//
// Each methodLogger returned by this method is a new instance. This is to
// Each MethodLogger returned by this method is a new instance. This is to
// generate sequence id within the call.
func (l *logger) GetMethodLogger(methodName string) MethodLogger {
s, m, err := grpcutil.ParseMethod(methodName)
@ -174,16 +174,16 @@ func (l *logger) GetMethodLogger(methodName string) MethodLogger {
return nil
}
if ml, ok := l.config.Methods[s+"/"+m]; ok {
return newMethodLogger(ml.Header, ml.Message)
return NewTruncatingMethodLogger(ml.Header, ml.Message)
}
if _, ok := l.config.Blacklist[s+"/"+m]; ok {
return nil
}
if ml, ok := l.config.Services[s]; ok {
return newMethodLogger(ml.Header, ml.Message)
return NewTruncatingMethodLogger(ml.Header, ml.Message)
}
if l.config.All == nil {
return nil
}
return newMethodLogger(l.config.All.Header, l.config.All.Message)
return NewTruncatingMethodLogger(l.config.All.Header, l.config.All.Message)
}

View File

@ -93,7 +93,7 @@ func (s) TestGetMethodLogger(t *testing.T) {
t.Errorf("in: %q, failed to create logger from config string", tc.in)
continue
}
ml := l.GetMethodLogger(tc.method).(*methodLogger)
ml := l.GetMethodLogger(tc.method).(*TruncatingMethodLogger)
if ml == nil {
t.Errorf("in: %q, method logger is nil, want non-nil", tc.in)
continue

View File

@ -57,7 +57,7 @@ func NewLoggerFromConfigString(s string) Logger {
return l
}
// fillMethodLoggerWithConfigString parses config, creates methodLogger and adds
// fillMethodLoggerWithConfigString parses config, creates TruncatingMethodLogger and adds
// it to the right map in the logger.
func (l *logger) fillMethodLoggerWithConfigString(config string) error {
// "" is invalid.

View File

@ -52,7 +52,9 @@ type MethodLogger interface {
Log(LogEntryConfig)
}
type methodLogger struct {
// TruncatingMethodLogger is a method logger that truncates headers and messages
// based on configured fields.
type TruncatingMethodLogger struct {
headerMaxLen, messageMaxLen uint64
callID uint64
@ -61,8 +63,9 @@ type methodLogger struct {
sink Sink // TODO(blog): make this plugable.
}
func newMethodLogger(h, m uint64) *methodLogger {
return &methodLogger{
// NewTruncatingMethodLogger returns a new truncating method logger.
func NewTruncatingMethodLogger(h, m uint64) *TruncatingMethodLogger {
return &TruncatingMethodLogger{
headerMaxLen: h,
messageMaxLen: m,
@ -75,8 +78,8 @@ func newMethodLogger(h, m uint64) *methodLogger {
// Build is an internal only method for building the proto message out of the
// input event. It's made public to enable other library to reuse as much logic
// in methodLogger as possible.
func (ml *methodLogger) Build(c LogEntryConfig) *pb.GrpcLogEntry {
// in TruncatingMethodLogger as possible.
func (ml *TruncatingMethodLogger) Build(c LogEntryConfig) *pb.GrpcLogEntry {
m := c.toProto()
timestamp, _ := ptypes.TimestampProto(time.Now())
m.Timestamp = timestamp
@ -95,11 +98,11 @@ func (ml *methodLogger) Build(c LogEntryConfig) *pb.GrpcLogEntry {
}
// Log creates a proto binary log entry, and logs it to the sink.
func (ml *methodLogger) Log(c LogEntryConfig) {
func (ml *TruncatingMethodLogger) Log(c LogEntryConfig) {
ml.sink.Write(ml.Build(c))
}
func (ml *methodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated bool) {
func (ml *TruncatingMethodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated bool) {
if ml.headerMaxLen == maxUInt {
return false
}
@ -129,7 +132,7 @@ func (ml *methodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated bool) {
return truncated
}
func (ml *methodLogger) truncateMessage(msgPb *pb.Message) (truncated bool) {
func (ml *TruncatingMethodLogger) truncateMessage(msgPb *pb.Message) (truncated bool) {
if ml.messageMaxLen == maxUInt {
return false
}

View File

@ -34,7 +34,7 @@ import (
func (s) TestLog(t *testing.T) {
idGen.reset()
ml := newMethodLogger(10, 10)
ml := NewTruncatingMethodLogger(10, 10)
// Set sink to testing buffer.
buf := bytes.NewBuffer(nil)
ml.sink = newWriterSink(buf)
@ -350,11 +350,11 @@ func (s) TestLog(t *testing.T) {
func (s) TestTruncateMetadataNotTruncated(t *testing.T) {
testCases := []struct {
ml *methodLogger
ml *TruncatingMethodLogger
mpPb *pb.Metadata
}{
{
ml: newMethodLogger(maxUInt, maxUInt),
ml: NewTruncatingMethodLogger(maxUInt, maxUInt),
mpPb: &pb.Metadata{
Entry: []*pb.MetadataEntry{
{Key: "", Value: []byte{1}},
@ -362,7 +362,7 @@ func (s) TestTruncateMetadataNotTruncated(t *testing.T) {
},
},
{
ml: newMethodLogger(2, maxUInt),
ml: NewTruncatingMethodLogger(2, maxUInt),
mpPb: &pb.Metadata{
Entry: []*pb.MetadataEntry{
{Key: "", Value: []byte{1}},
@ -370,7 +370,7 @@ func (s) TestTruncateMetadataNotTruncated(t *testing.T) {
},
},
{
ml: newMethodLogger(1, maxUInt),
ml: NewTruncatingMethodLogger(1, maxUInt),
mpPb: &pb.Metadata{
Entry: []*pb.MetadataEntry{
{Key: "", Value: nil},
@ -378,7 +378,7 @@ func (s) TestTruncateMetadataNotTruncated(t *testing.T) {
},
},
{
ml: newMethodLogger(2, maxUInt),
ml: NewTruncatingMethodLogger(2, maxUInt),
mpPb: &pb.Metadata{
Entry: []*pb.MetadataEntry{
{Key: "", Value: []byte{1, 1}},
@ -386,7 +386,7 @@ func (s) TestTruncateMetadataNotTruncated(t *testing.T) {
},
},
{
ml: newMethodLogger(2, maxUInt),
ml: NewTruncatingMethodLogger(2, maxUInt),
mpPb: &pb.Metadata{
Entry: []*pb.MetadataEntry{
{Key: "", Value: []byte{1}},
@ -397,7 +397,7 @@ func (s) TestTruncateMetadataNotTruncated(t *testing.T) {
// "grpc-trace-bin" is kept in log but not counted towards the size
// limit.
{
ml: newMethodLogger(1, maxUInt),
ml: NewTruncatingMethodLogger(1, maxUInt),
mpPb: &pb.Metadata{
Entry: []*pb.MetadataEntry{
{Key: "", Value: []byte{1}},
@ -417,13 +417,13 @@ func (s) TestTruncateMetadataNotTruncated(t *testing.T) {
func (s) TestTruncateMetadataTruncated(t *testing.T) {
testCases := []struct {
ml *methodLogger
ml *TruncatingMethodLogger
mpPb *pb.Metadata
entryLen int
}{
{
ml: newMethodLogger(2, maxUInt),
ml: NewTruncatingMethodLogger(2, maxUInt),
mpPb: &pb.Metadata{
Entry: []*pb.MetadataEntry{
{Key: "", Value: []byte{1, 1, 1}},
@ -432,7 +432,7 @@ func (s) TestTruncateMetadataTruncated(t *testing.T) {
entryLen: 0,
},
{
ml: newMethodLogger(2, maxUInt),
ml: NewTruncatingMethodLogger(2, maxUInt),
mpPb: &pb.Metadata{
Entry: []*pb.MetadataEntry{
{Key: "", Value: []byte{1}},
@ -443,7 +443,7 @@ func (s) TestTruncateMetadataTruncated(t *testing.T) {
entryLen: 2,
},
{
ml: newMethodLogger(2, maxUInt),
ml: NewTruncatingMethodLogger(2, maxUInt),
mpPb: &pb.Metadata{
Entry: []*pb.MetadataEntry{
{Key: "", Value: []byte{1, 1}},
@ -453,7 +453,7 @@ func (s) TestTruncateMetadataTruncated(t *testing.T) {
entryLen: 1,
},
{
ml: newMethodLogger(2, maxUInt),
ml: NewTruncatingMethodLogger(2, maxUInt),
mpPb: &pb.Metadata{
Entry: []*pb.MetadataEntry{
{Key: "", Value: []byte{1}},
@ -478,23 +478,23 @@ func (s) TestTruncateMetadataTruncated(t *testing.T) {
func (s) TestTruncateMessageNotTruncated(t *testing.T) {
testCases := []struct {
ml *methodLogger
ml *TruncatingMethodLogger
msgPb *pb.Message
}{
{
ml: newMethodLogger(maxUInt, maxUInt),
ml: NewTruncatingMethodLogger(maxUInt, maxUInt),
msgPb: &pb.Message{
Data: []byte{1},
},
},
{
ml: newMethodLogger(maxUInt, 3),
ml: NewTruncatingMethodLogger(maxUInt, 3),
msgPb: &pb.Message{
Data: []byte{1, 1},
},
},
{
ml: newMethodLogger(maxUInt, 2),
ml: NewTruncatingMethodLogger(maxUInt, 2),
msgPb: &pb.Message{
Data: []byte{1, 1},
},
@ -511,13 +511,13 @@ func (s) TestTruncateMessageNotTruncated(t *testing.T) {
func (s) TestTruncateMessageTruncated(t *testing.T) {
testCases := []struct {
ml *methodLogger
ml *TruncatingMethodLogger
msgPb *pb.Message
oldLength uint32
}{
{
ml: newMethodLogger(maxUInt, 2),
ml: NewTruncatingMethodLogger(maxUInt, 2),
msgPb: &pb.Message{
Length: 3,
Data: []byte{1, 1, 1},

View File

@ -0,0 +1,36 @@
/*
*
* Copyright 2022 gRPC 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 envconfig
import "os"
const (
envObservabilityConfig = "GRPC_GCP_OBSERVABILITY_CONFIG"
envObservabilityConfigFile = "GRPC_GCP_OBSERVABILITY_CONFIG_FILE"
)
var (
// ObservabilityConfig is the json configuration for the gcp/observability
// package specified directly in the envObservabilityConfig env var.
ObservabilityConfig = os.Getenv(envObservabilityConfig)
// ObservabilityConfigFile is the json configuration for the
// gcp/observability specified in a file with the location specified in
// envObservabilityConfigFile env var.
ObservabilityConfigFile = os.Getenv(envObservabilityConfigFile)
)