1// Copyright 2018 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package etcdserverpb
16
17import (
18	"fmt"
19	"strings"
20
21	proto "github.com/golang/protobuf/proto"
22)
23
24// InternalRaftStringer implements custom proto Stringer:
25// redact password, replace value fields with value_size fields.
26type InternalRaftStringer struct {
27	Request *InternalRaftRequest
28}
29
30func (as *InternalRaftStringer) String() string {
31	switch {
32	case as.Request.LeaseGrant != nil:
33		return fmt.Sprintf("header:<%s> lease_grant:<ttl:%d-second id:%016x>",
34			as.Request.Header.String(),
35			as.Request.LeaseGrant.TTL,
36			as.Request.LeaseGrant.ID,
37		)
38	case as.Request.LeaseRevoke != nil:
39		return fmt.Sprintf("header:<%s> lease_revoke:<id:%016x>",
40			as.Request.Header.String(),
41			as.Request.LeaseRevoke.ID,
42		)
43	case as.Request.Authenticate != nil:
44		return fmt.Sprintf("header:<%s> authenticate:<name:%s simple_token:%s>",
45			as.Request.Header.String(),
46			as.Request.Authenticate.Name,
47			as.Request.Authenticate.SimpleToken,
48		)
49	case as.Request.AuthUserAdd != nil:
50		return fmt.Sprintf("header:<%s> auth_user_add:<name:%s>",
51			as.Request.Header.String(),
52			as.Request.AuthUserAdd.Name,
53		)
54	case as.Request.AuthUserChangePassword != nil:
55		return fmt.Sprintf("header:<%s> auth_user_change_password:<name:%s>",
56			as.Request.Header.String(),
57			as.Request.AuthUserChangePassword.Name,
58		)
59	case as.Request.Put != nil:
60		return fmt.Sprintf("header:<%s> put:<%s>",
61			as.Request.Header.String(),
62			NewLoggablePutRequest(as.Request.Put).String(),
63		)
64	case as.Request.Txn != nil:
65		return fmt.Sprintf("header:<%s> txn:<%s>",
66			as.Request.Header.String(),
67			NewLoggableTxnRequest(as.Request.Txn).String(),
68		)
69	default:
70		// nothing to redact
71	}
72	return as.Request.String()
73}
74
75// txnRequestStringer implements a custom proto String to replace value bytes fields with value size
76// fields in any nested txn and put operations.
77type txnRequestStringer struct {
78	Request *TxnRequest
79}
80
81func NewLoggableTxnRequest(request *TxnRequest) *txnRequestStringer {
82	return &txnRequestStringer{request}
83}
84
85func (as *txnRequestStringer) String() string {
86	var compare []string
87	for _, c := range as.Request.Compare {
88		switch cv := c.TargetUnion.(type) {
89		case *Compare_Value:
90			compare = append(compare, newLoggableValueCompare(c, cv).String())
91		default:
92			// nothing to redact
93			compare = append(compare, c.String())
94		}
95	}
96	var success []string
97	for _, s := range as.Request.Success {
98		success = append(success, newLoggableRequestOp(s).String())
99	}
100	var failure []string
101	for _, f := range as.Request.Failure {
102		failure = append(failure, newLoggableRequestOp(f).String())
103	}
104	return fmt.Sprintf("compare:<%s> success:<%s> failure:<%s>",
105		strings.Join(compare, " "),
106		strings.Join(success, " "),
107		strings.Join(failure, " "),
108	)
109}
110
111// requestOpStringer implements a custom proto String to replace value bytes fields with value
112// size fields in any nested txn and put operations.
113type requestOpStringer struct {
114	Op *RequestOp
115}
116
117func newLoggableRequestOp(op *RequestOp) *requestOpStringer {
118	return &requestOpStringer{op}
119}
120
121func (as *requestOpStringer) String() string {
122	switch op := as.Op.Request.(type) {
123	case *RequestOp_RequestPut:
124		return fmt.Sprintf("request_put:<%s>", NewLoggablePutRequest(op.RequestPut).String())
125	case *RequestOp_RequestTxn:
126		return fmt.Sprintf("request_txn:<%s>", NewLoggableTxnRequest(op.RequestTxn).String())
127	default:
128		// nothing to redact
129	}
130	return as.Op.String()
131}
132
133// loggableValueCompare implements a custom proto String for Compare.Value union member types to
134// replace the value bytes field with a value size field.
135// To preserve proto encoding of the key and range_end bytes, a faked out proto type is used here.
136type loggableValueCompare struct {
137	Result    Compare_CompareResult `protobuf:"varint,1,opt,name=result,proto3,enum=etcdserverpb.Compare_CompareResult"`
138	Target    Compare_CompareTarget `protobuf:"varint,2,opt,name=target,proto3,enum=etcdserverpb.Compare_CompareTarget"`
139	Key       []byte                `protobuf:"bytes,3,opt,name=key,proto3"`
140	ValueSize int64                 `protobuf:"varint,7,opt,name=value_size,proto3"`
141	RangeEnd  []byte                `protobuf:"bytes,64,opt,name=range_end,proto3"`
142}
143
144func newLoggableValueCompare(c *Compare, cv *Compare_Value) *loggableValueCompare {
145	return &loggableValueCompare{
146		c.Result,
147		c.Target,
148		c.Key,
149		int64(len(cv.Value)),
150		c.RangeEnd,
151	}
152}
153
154func (m *loggableValueCompare) Reset()         { *m = loggableValueCompare{} }
155func (m *loggableValueCompare) String() string { return proto.CompactTextString(m) }
156func (*loggableValueCompare) ProtoMessage()    {}
157
158// loggablePutRequest implements a custom proto String to replace value bytes field with a value
159// size field.
160// To preserve proto encoding of the key bytes, a faked out proto type is used here.
161type loggablePutRequest struct {
162	Key         []byte `protobuf:"bytes,1,opt,name=key,proto3"`
163	ValueSize   int64  `protobuf:"varint,2,opt,name=value_size,proto3"`
164	Lease       int64  `protobuf:"varint,3,opt,name=lease,proto3"`
165	PrevKv      bool   `protobuf:"varint,4,opt,name=prev_kv,proto3"`
166	IgnoreValue bool   `protobuf:"varint,5,opt,name=ignore_value,proto3"`
167	IgnoreLease bool   `protobuf:"varint,6,opt,name=ignore_lease,proto3"`
168}
169
170func NewLoggablePutRequest(request *PutRequest) *loggablePutRequest {
171	return &loggablePutRequest{
172		request.Key,
173		int64(len(request.Value)),
174		request.Lease,
175		request.PrevKv,
176		request.IgnoreValue,
177		request.IgnoreLease,
178	}
179}
180
181func (m *loggablePutRequest) Reset()         { *m = loggablePutRequest{} }
182func (m *loggablePutRequest) String() string { return proto.CompactTextString(m) }
183func (*loggablePutRequest) ProtoMessage()    {}
184