/*
 *
 * Copyright 2018 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 channelz

import (
	
	
	
	

	
)

const (
	defaultMaxTraceEntry int32 = 30
)

var maxTraceEntry = defaultMaxTraceEntry

// SetMaxTraceEntry sets maximum number of trace entries per entity (i.e.
// channel/subchannel).  Setting it to 0 will disable channel tracing.
func ( int32) {
	atomic.StoreInt32(&maxTraceEntry, )
}

// ResetMaxTraceEntryToDefault resets the maximum number of trace entries per
// entity to default.
func () {
	atomic.StoreInt32(&maxTraceEntry, defaultMaxTraceEntry)
}

func () int {
	 := atomic.LoadInt32(&maxTraceEntry)
	return int()
}

// traceEvent is an internal representation of a single trace event
type traceEvent struct {
	// Desc is a simple description of the trace event.
	Desc string
	// Severity states the severity of this trace event.
	Severity Severity
	// Timestamp is the event time.
	Timestamp time.Time
	// RefID is the id of the entity that gets referenced in the event. RefID is 0 if no other entity is
	// involved in this event.
	// e.g. SubChannel (id: 4[]) Created. --> RefID = 4, RefName = "" (inside [])
	RefID int64
	// RefName is the reference name for the entity that gets referenced in the event.
	RefName string
	// RefType indicates the referenced entity type, i.e Channel or SubChannel.
	RefType RefChannelType
}

// TraceEvent is what the caller of AddTraceEvent should provide to describe the
// event to be added to the channel trace.
//
// The Parent field is optional. It is used for an event that will be recorded
// in the entity's parent trace.
type TraceEvent struct {
	Desc     string
	Severity Severity
	Parent   *TraceEvent
}

// ChannelTrace provides tracing information for a channel.
// It tracks various events and metadata related to the channel's lifecycle
// and operations.
type ChannelTrace struct {
	cm          *channelMap
	clearCalled bool
	// The time when the trace was created.
	CreationTime time.Time
	// A counter for the number of events recorded in the
	// trace.
	EventNum int64
	mu       sync.Mutex
	// A slice of traceEvent pointers representing the events recorded for
	// this channel.
	Events []*traceEvent
}

func ( *ChannelTrace) () *ChannelTrace {
	return &ChannelTrace{
		CreationTime: .CreationTime,
		EventNum:     .EventNum,
		Events:       append(([]*traceEvent)(nil), .Events...),
	}
}

func ( *ChannelTrace) ( *traceEvent) {
	.mu.Lock()
	if len(.Events) == getMaxTraceEntry() {
		 := .Events[0]
		.Events = .Events[1:]
		if .RefID != 0 {
			// start recursive cleanup in a goroutine to not block the call originated from grpc.
			go func() {
				// need to acquire c.cm.mu lock to call the unlocked attemptCleanup func.
				.cm.mu.Lock()
				.cm.decrTraceRefCount(.RefID)
				.cm.mu.Unlock()
			}()
		}
	}
	.Timestamp = time.Now()
	.Events = append(.Events, )
	.EventNum++
	.mu.Unlock()
}

func ( *ChannelTrace) () {
	if .clearCalled {
		return
	}
	.clearCalled = true
	.mu.Lock()
	for ,  := range .Events {
		if .RefID != 0 {
			// caller should have already held the c.cm.mu lock.
			.cm.decrTraceRefCount(.RefID)
		}
	}
	.mu.Unlock()
}

// Severity is the severity level of a trace event.
// The canonical enumeration of all valid values is here:
// https://github.com/grpc/grpc-proto/blob/9b13d199cc0d4703c7ea26c9c330ba695866eb23/grpc/channelz/v1/channelz.proto#L126.
type Severity int

const (
	// CtUnknown indicates unknown severity of a trace event.
	CtUnknown Severity = iota
	// CtInfo indicates info level severity of a trace event.
	CtInfo
	// CtWarning indicates warning level severity of a trace event.
	CtWarning
	// CtError indicates error level severity of a trace event.
	CtError
)

// RefChannelType is the type of the entity being referenced in a trace event.
type RefChannelType int

const (
	// RefUnknown indicates an unknown entity type, the zero value for this type.
	RefUnknown RefChannelType = iota
	// RefChannel indicates the referenced entity is a Channel.
	RefChannel
	// RefSubChannel indicates the referenced entity is a SubChannel.
	RefSubChannel
	// RefServer indicates the referenced entity is a Server.
	RefServer
	// RefListenSocket indicates the referenced entity is a ListenSocket.
	RefListenSocket
	// RefNormalSocket indicates the referenced entity is a NormalSocket.
	RefNormalSocket
)

var refChannelTypeToString = map[RefChannelType]string{
	RefUnknown:      "Unknown",
	RefChannel:      "Channel",
	RefSubChannel:   "SubChannel",
	RefServer:       "Server",
	RefListenSocket: "ListenSocket",
	RefNormalSocket: "NormalSocket",
}

// String returns a string representation of the RefChannelType
func ( RefChannelType) () string {
	return refChannelTypeToString[]
}

// AddTraceEvent adds trace related to the entity with specified id, using the
// provided TraceEventDesc.
//
// If channelz is not turned ON, this will simply log the event descriptions.
func ( grpclog.DepthLoggerV2,  Entity,  int,  *TraceEvent) {
	// Log only the trace description associated with the bottom most entity.
	 := fmt.Sprintf("[%s]%s", , .Desc)
	switch .Severity {
	case CtUnknown, CtInfo:
		.InfoDepth(+1, )
	case CtWarning:
		.WarningDepth(+1, )
	case CtError:
		.ErrorDepth(+1, )
	}

	if getMaxTraceEntry() == 0 {
		return
	}
	if IsOn() {
		db.traceEvent(.id(), )
	}
}