github.com/Rookout/GoSDK@v0.1.48/pkg/com_ws/output_ws.go (about)

     1  package com_ws
     2  
     3  import (
     4  	"io"
     5  	"sync"
     6  	"sync/atomic"
     7  	"time"
     8  
     9  	"github.com/Rookout/GoSDK/pkg/common"
    10  	"github.com/Rookout/GoSDK/pkg/config"
    11  	"github.com/Rookout/GoSDK/pkg/logger"
    12  	"github.com/Rookout/GoSDK/pkg/processor/namespaces"
    13  	pb "github.com/Rookout/GoSDK/pkg/protobuf"
    14  	"github.com/Rookout/GoSDK/pkg/rookoutErrors"
    15  	"github.com/Rookout/GoSDK/pkg/types"
    16  	"github.com/Rookout/GoSDK/pkg/utils"
    17  	"google.golang.org/protobuf/types/known/timestamppb"
    18  )
    19  
    20  type Output interface {
    21  	SetAgentID(agentID string)
    22  
    23  	SendUserMessage(augID types.AugID, messageID string, arguments namespaces.Namespace)
    24  	SendRuleStatus(augID types.AugID, active string, errIn rookoutErrors.RookoutError) error
    25  	SendWarning(augID types.AugID, err rookoutErrors.RookoutError) error
    26  	SendError(augID types.AugID, err rookoutErrors.RookoutError) error
    27  	SendOutputQueueFullWarning(augID types.AugID)
    28  
    29  	SetAgentCom(agentCom AgentCom)
    30  
    31  	StopSendingMessages()
    32  }
    33  
    34  type outputWs struct {
    35  	agentID       string
    36  	agentCom      AgentCom
    37  	closed        atomic.Value
    38  	skippedAugIDs *SyncSet
    39  }
    40  
    41  func NewOutputWs() *outputWs {
    42  	o := &outputWs{
    43  		closed:        atomic.Value{},
    44  		skippedAugIDs: newSyncSet(),
    45  	}
    46  	o.closed.Store(false)
    47  	return o
    48  }
    49  
    50  func (d *outputWs) SetAgentCom(agentCom AgentCom) {
    51  	d.agentCom = agentCom
    52  }
    53  
    54  func (d *outputWs) SetAgentID(agentID string) {
    55  	d.agentID = agentID
    56  }
    57  
    58  func (d *outputWs) SendUserMessage(augID types.AugID, messageID string, arguments namespaces.Namespace) {
    59  	utils.CreateGoroutine(func() {
    60  		defer func() {
    61  			if closer, ok := arguments.(io.Closer); ok {
    62  				_ = closer.Close()
    63  			}
    64  		}()
    65  
    66  		err := d.sendUserMessage(augID, messageID, arguments)
    67  		if err != nil {
    68  			logger.Logger().WithError(err).Warningf("Unable to send user message, aug id: %s", augID)
    69  		}
    70  	})
    71  }
    72  
    73  func (d *outputWs) sendUserMessage(augID types.AugID, messageID string, arguments namespaces.Namespace) error {
    74  	if d.closed.Load().(bool) {
    75  		return nil
    76  	}
    77  
    78  	msg := &pb.AugReportMessage{
    79  		AgentId:  d.agentID,
    80  		AugID:    augID,
    81  		ReportID: messageID,
    82  	}
    83  	if config.OutputWsConfig().ProtobufVersion2 {
    84  		serializer := namespaces.NewNamespaceSerializer2(arguments, true)
    85  		msg.Arguments2 = serializer.Variant2
    86  		msg.StringsCache = serializer.StringCache
    87  	} else {
    88  		serializer := namespaces.NewNamespaceSerializer(arguments, true)
    89  		msg.Arguments = serializer.Variant
    90  	}
    91  	buf, err := common.WrapMsgInEnvelope(msg)
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	rookoutErr := d.agentCom.Send(buf)
    97  	if rookoutErr != nil && rookoutErr.GetType() == "RookOutputQueueFull" {
    98  		d.SendOutputQueueFullWarning(augID)
    99  		return rookoutErr
   100  	}
   101  
   102  	d.skippedAugIDs.Remove(augID)
   103  	return nil
   104  }
   105  
   106  func (d *outputWs) SendRuleStatus(augID types.AugID, active string, errIn rookoutErrors.RookoutError) error {
   107  	if d.closed.Load().(bool) {
   108  		return nil
   109  	}
   110  
   111  	if active == "Deleted" {
   112  		d.skippedAugIDs.Remove(augID)
   113  	}
   114  
   115  	stat := &pb.RuleStatusMessage{
   116  		AgentId: d.agentID,
   117  		RuleId:  augID,
   118  		Active:  active,
   119  	}
   120  	if errIn != nil {
   121  		serializer := namespaces.NewNamespaceSerializer(namespaces.NewGoObjectNamespace(errIn), true)
   122  		stat.Error = serializer.GetErrorValue()
   123  	}
   124  
   125  	buf, err := common.WrapMsgInEnvelope(stat)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	return d.agentCom.Send(buf)
   130  }
   131  
   132  func (d *outputWs) SendWarning(augID types.AugID, err rookoutErrors.RookoutError) error {
   133  	return d.SendRuleStatus(augID, "Warning", err)
   134  }
   135  
   136  func (d *outputWs) SendError(augID types.AugID, err rookoutErrors.RookoutError) error {
   137  	return d.SendRuleStatus(augID, "Error", err)
   138  }
   139  
   140  func (d *outputWs) SendLogMessage(level pb.LogMessage_LogLevel, time time.Time, filename string, lineno int, text string, arguments map[string]interface{}) error {
   141  	if d.closed.Load().(bool) || d.agentCom == nil {
   142  		return nil
   143  	}
   144  
   145  	argumentsNamespace := namespaces.NewEmptyContainerNamespace()
   146  	parametersNamespace := namespaces.NewEmptyContainerNamespace()
   147  	for k, v := range arguments {
   148  		if v == nil {
   149  			continue
   150  		}
   151  
   152  		if k == "error" {
   153  			_ = argumentsNamespace.WriteAttribute("exc", namespaces.NewGoObjectNamespace(v))
   154  		} else {
   155  			_ = parametersNamespace.WriteAttribute(k, namespaces.NewGoObjectNamespace(v))
   156  		}
   157  	}
   158  	_ = argumentsNamespace.WriteAttribute("args", parametersNamespace)
   159  	serializer := namespaces.NewNamespaceSerializer(argumentsNamespace, false)
   160  
   161  	timestamp := timestamppb.New(time)
   162  	msg := &pb.LogMessage{
   163  		Timestamp:        timestamp,
   164  		AgentId:          d.agentID,
   165  		Level:            level,
   166  		Filename:         filename,
   167  		Line:             uint32(lineno),
   168  		Text:             text,
   169  		FormattedMessage: text,
   170  		Arguments:        []*pb.Variant{serializer.Variant},
   171  		LegacyArguments:  serializer.Variant,
   172  	}
   173  	buf, err := common.WrapMsgInEnvelope(msg)
   174  	if err != nil {
   175  		return err
   176  	}
   177  	return d.agentCom.Send(buf)
   178  }
   179  
   180  func (d *outputWs) StopSendingMessages() {
   181  	d.closed.Store(true)
   182  }
   183  
   184  func (d *outputWs) SendOutputQueueFullWarning(augID types.AugID) {
   185  	if d.skippedAugIDs.Contains(augID) {
   186  		return
   187  	}
   188  
   189  	d.skippedAugIDs.Add(augID)
   190  	_ = d.SendRuleStatus(augID, "Warning", rookoutErrors.NewRookOutputQueueFull())
   191  	logger.Logger().Warning("Skipping aug (" + augID + ") execution because the queue is full")
   192  }
   193  
   194  type SyncSet struct {
   195  	internalMap map[types.AugID]struct{}
   196  	lock        sync.Mutex
   197  }
   198  
   199  func newSyncSet() *SyncSet {
   200  	return &SyncSet{internalMap: make(map[types.AugID]struct{})}
   201  }
   202  
   203  func (s *SyncSet) Add(value interface{}) {
   204  	s.lock.Lock()
   205  	defer s.lock.Unlock()
   206  
   207  	s.internalMap[value.(types.AugID)] = struct{}{}
   208  }
   209  
   210  func (s *SyncSet) Remove(value interface{}) {
   211  	s.lock.Lock()
   212  	defer s.lock.Unlock()
   213  
   214  	delete(s.internalMap, value.(types.AugID))
   215  }
   216  
   217  func (s *SyncSet) Contains(value interface{}) bool {
   218  	s.lock.Lock()
   219  	defer s.lock.Unlock()
   220  
   221  	_, ok := s.internalMap[value.(types.AugID)]
   222  	return ok
   223  }