github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/msgtrace.go (about) 1 // Copyright 2024 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package server 15 16 import ( 17 "bytes" 18 "encoding/json" 19 "fmt" 20 "math/rand" 21 "strconv" 22 "strings" 23 "sync/atomic" 24 "time" 25 ) 26 27 const ( 28 MsgTraceDest = "Nats-Trace-Dest" 29 MsgTraceHop = "Nats-Trace-Hop" 30 MsgTraceOriginAccount = "Nats-Trace-Origin-Account" 31 MsgTraceOnly = "Nats-Trace-Only" 32 33 // External trace header. Note that this header is normally in lower 34 // case (https://www.w3.org/TR/trace-context/#header-name). Vendors 35 // MUST expect the header in any case (upper, lower, mixed), and 36 // SHOULD send the header name in lowercase. 37 traceParentHdr = "traceparent" 38 ) 39 40 type MsgTraceType string 41 42 // Type of message trace events in the MsgTraceEvents list. 43 // This is needed to unmarshal the list. 44 const ( 45 MsgTraceIngressType = "in" 46 MsgTraceSubjectMappingType = "sm" 47 MsgTraceStreamExportType = "se" 48 MsgTraceServiceImportType = "si" 49 MsgTraceJetStreamType = "js" 50 MsgTraceEgressType = "eg" 51 ) 52 53 type MsgTraceEvent struct { 54 Server ServerInfo `json:"server"` 55 Request MsgTraceRequest `json:"request"` 56 Hops int `json:"hops,omitempty"` 57 Events MsgTraceEvents `json:"events"` 58 } 59 60 type MsgTraceRequest struct { 61 // We are not making this an http.Header so that header name case is preserved. 62 Header map[string][]string `json:"header,omitempty"` 63 MsgSize int `json:"msgsize,omitempty"` 64 } 65 66 type MsgTraceEvents []MsgTrace 67 68 type MsgTrace interface { 69 new() MsgTrace 70 typ() MsgTraceType 71 } 72 73 type MsgTraceBase struct { 74 Type MsgTraceType `json:"type"` 75 Timestamp time.Time `json:"ts"` 76 } 77 78 type MsgTraceIngress struct { 79 MsgTraceBase 80 Kind int `json:"kind"` 81 CID uint64 `json:"cid"` 82 Name string `json:"name,omitempty"` 83 Account string `json:"acc"` 84 Subject string `json:"subj"` 85 Error string `json:"error,omitempty"` 86 } 87 88 type MsgTraceSubjectMapping struct { 89 MsgTraceBase 90 MappedTo string `json:"to"` 91 } 92 93 type MsgTraceStreamExport struct { 94 MsgTraceBase 95 Account string `json:"acc"` 96 To string `json:"to"` 97 } 98 99 type MsgTraceServiceImport struct { 100 MsgTraceBase 101 Account string `json:"acc"` 102 From string `json:"from"` 103 To string `json:"to"` 104 } 105 106 type MsgTraceJetStream struct { 107 MsgTraceBase 108 Stream string `json:"stream"` 109 Subject string `json:"subject,omitempty"` 110 NoInterest bool `json:"nointerest,omitempty"` 111 Error string `json:"error,omitempty"` 112 } 113 114 type MsgTraceEgress struct { 115 MsgTraceBase 116 Kind int `json:"kind"` 117 CID uint64 `json:"cid"` 118 Name string `json:"name,omitempty"` 119 Hop string `json:"hop,omitempty"` 120 Account string `json:"acc,omitempty"` 121 Subscription string `json:"sub,omitempty"` 122 Queue string `json:"queue,omitempty"` 123 Error string `json:"error,omitempty"` 124 125 // This is for applications that unmarshal the trace events 126 // and want to link an egress to route/leaf/gateway with 127 // the MsgTraceEvent from that server. 128 Link *MsgTraceEvent `json:"-"` 129 } 130 131 // ------------------------------------------------------------- 132 133 func (t MsgTraceBase) typ() MsgTraceType { return t.Type } 134 func (MsgTraceIngress) new() MsgTrace { return &MsgTraceIngress{} } 135 func (MsgTraceSubjectMapping) new() MsgTrace { return &MsgTraceSubjectMapping{} } 136 func (MsgTraceStreamExport) new() MsgTrace { return &MsgTraceStreamExport{} } 137 func (MsgTraceServiceImport) new() MsgTrace { return &MsgTraceServiceImport{} } 138 func (MsgTraceJetStream) new() MsgTrace { return &MsgTraceJetStream{} } 139 func (MsgTraceEgress) new() MsgTrace { return &MsgTraceEgress{} } 140 141 var msgTraceInterfaces = map[MsgTraceType]MsgTrace{ 142 MsgTraceIngressType: MsgTraceIngress{}, 143 MsgTraceSubjectMappingType: MsgTraceSubjectMapping{}, 144 MsgTraceStreamExportType: MsgTraceStreamExport{}, 145 MsgTraceServiceImportType: MsgTraceServiceImport{}, 146 MsgTraceJetStreamType: MsgTraceJetStream{}, 147 MsgTraceEgressType: MsgTraceEgress{}, 148 } 149 150 func (t *MsgTraceEvents) UnmarshalJSON(data []byte) error { 151 var raw []json.RawMessage 152 err := json.Unmarshal(data, &raw) 153 if err != nil { 154 return err 155 } 156 *t = make(MsgTraceEvents, len(raw)) 157 var tt MsgTraceBase 158 for i, r := range raw { 159 if err = json.Unmarshal(r, &tt); err != nil { 160 return err 161 } 162 tr, ok := msgTraceInterfaces[tt.Type] 163 if !ok { 164 return fmt.Errorf("unknown trace type %v", tt.Type) 165 } 166 te := tr.new() 167 if err := json.Unmarshal(r, te); err != nil { 168 return err 169 } 170 (*t)[i] = te 171 } 172 return nil 173 } 174 175 func getTraceAs[T MsgTrace](e any) *T { 176 v, ok := e.(*T) 177 if ok { 178 return v 179 } 180 return nil 181 } 182 183 func (t *MsgTraceEvent) Ingress() *MsgTraceIngress { 184 if len(t.Events) < 1 { 185 return nil 186 } 187 return getTraceAs[MsgTraceIngress](t.Events[0]) 188 } 189 190 func (t *MsgTraceEvent) SubjectMapping() *MsgTraceSubjectMapping { 191 for _, e := range t.Events { 192 if e.typ() == MsgTraceSubjectMappingType { 193 return getTraceAs[MsgTraceSubjectMapping](e) 194 } 195 } 196 return nil 197 } 198 199 func (t *MsgTraceEvent) StreamExports() []*MsgTraceStreamExport { 200 var se []*MsgTraceStreamExport 201 for _, e := range t.Events { 202 if e.typ() == MsgTraceStreamExportType { 203 se = append(se, getTraceAs[MsgTraceStreamExport](e)) 204 } 205 } 206 return se 207 } 208 209 func (t *MsgTraceEvent) ServiceImports() []*MsgTraceServiceImport { 210 var si []*MsgTraceServiceImport 211 for _, e := range t.Events { 212 if e.typ() == MsgTraceServiceImportType { 213 si = append(si, getTraceAs[MsgTraceServiceImport](e)) 214 } 215 } 216 return si 217 } 218 219 func (t *MsgTraceEvent) JetStream() *MsgTraceJetStream { 220 for _, e := range t.Events { 221 if e.typ() == MsgTraceJetStreamType { 222 return getTraceAs[MsgTraceJetStream](e) 223 } 224 } 225 return nil 226 } 227 228 func (t *MsgTraceEvent) Egresses() []*MsgTraceEgress { 229 var eg []*MsgTraceEgress 230 for _, e := range t.Events { 231 if e.typ() == MsgTraceEgressType { 232 eg = append(eg, getTraceAs[MsgTraceEgress](e)) 233 } 234 } 235 return eg 236 } 237 238 const ( 239 errMsgTraceOnlyNoSupport = "Not delivered because remote does not support message tracing" 240 errMsgTraceNoSupport = "Message delivered but remote does not support message tracing so no trace event generated from there" 241 errMsgTraceNoEcho = "Not delivered because of no echo" 242 errMsgTracePubViolation = "Not delivered because publish denied for this subject" 243 errMsgTraceSubDeny = "Not delivered because subscription denies this subject" 244 errMsgTraceSubClosed = "Not delivered because subscription is closed" 245 errMsgTraceClientClosed = "Not delivered because client is closed" 246 errMsgTraceAutoSubExceeded = "Not delivered because auto-unsubscribe exceeded" 247 ) 248 249 type msgTrace struct { 250 ready int32 251 srv *Server 252 acc *Account 253 // Origin account name, set only if acc is nil when acc lookup failed. 254 oan string 255 dest string 256 event *MsgTraceEvent 257 js *MsgTraceJetStream 258 hop string 259 nhop string 260 tonly bool // Will only trace the message, not do delivery. 261 ct compressionType 262 } 263 264 // This will be false outside of the tests, so when building the server binary, 265 // any code where you see `if msgTraceRunInTests` statement will be compiled 266 // out, so this will have no performance penalty. 267 var ( 268 msgTraceRunInTests bool 269 msgTraceCheckSupport bool 270 ) 271 272 // Returns the message trace object, if message is being traced, 273 // and `true` if we want to only trace, not actually deliver the message. 274 func (c *client) isMsgTraceEnabled() (*msgTrace, bool) { 275 t := c.pa.trace 276 if t == nil { 277 return nil, false 278 } 279 return t, t.tonly 280 } 281 282 // For LEAF/ROUTER/GATEWAY, return false if the remote does not support 283 // message tracing (important if the tracing requests trace-only). 284 func (c *client) msgTraceSupport() bool { 285 // Exclude client connection from the protocol check. 286 return c.kind == CLIENT || c.opts.Protocol >= MsgTraceProto 287 } 288 289 func getConnName(c *client) string { 290 switch c.kind { 291 case ROUTER: 292 if n := c.route.remoteName; n != _EMPTY_ { 293 return n 294 } 295 case GATEWAY: 296 if n := c.gw.remoteName; n != _EMPTY_ { 297 return n 298 } 299 case LEAF: 300 if n := c.leaf.remoteServer; n != _EMPTY_ { 301 return n 302 } 303 } 304 return c.opts.Name 305 } 306 307 func getCompressionType(cts string) compressionType { 308 if cts == _EMPTY_ { 309 return noCompression 310 } 311 cts = strings.ToLower(cts) 312 if strings.Contains(cts, "snappy") || strings.Contains(cts, "s2") { 313 return snappyCompression 314 } 315 if strings.Contains(cts, "gzip") { 316 return gzipCompression 317 } 318 return unsupportedCompression 319 } 320 321 func (c *client) initMsgTrace() *msgTrace { 322 // The code in the "if" statement is only running in test mode. 323 if msgTraceRunInTests { 324 // Check the type of client that tries to initialize a trace struct. 325 if !(c.kind == CLIENT || c.kind == ROUTER || c.kind == GATEWAY || c.kind == LEAF) { 326 panic(fmt.Sprintf("Unexpected client type %q trying to initialize msgTrace", c.kindString())) 327 } 328 // In some tests, we want to make a server behave like an old server 329 // and so even if a trace header is received, we want the server to 330 // simply ignore it. 331 if msgTraceCheckSupport { 332 if c.srv == nil || c.srv.getServerProto() < MsgTraceProto { 333 return nil 334 } 335 } 336 } 337 if c.pa.hdr <= 0 { 338 return nil 339 } 340 hdr := c.msgBuf[:c.pa.hdr] 341 headers, external := genHeaderMapIfTraceHeadersPresent(hdr) 342 if len(headers) == 0 { 343 return nil 344 } 345 // Little helper to give us the first value of a given header, or _EMPTY_ 346 // if key is not present. 347 getHdrVal := func(key string) string { 348 vv, ok := headers[key] 349 if !ok { 350 return _EMPTY_ 351 } 352 return vv[0] 353 } 354 ct := getCompressionType(getHdrVal(acceptEncodingHeader)) 355 var ( 356 dest string 357 traceOnly bool 358 ) 359 // Check for traceOnly only if not external. 360 if !external { 361 if to := getHdrVal(MsgTraceOnly); to != _EMPTY_ { 362 tos := strings.ToLower(to) 363 switch tos { 364 case "1", "true", "on": 365 traceOnly = true 366 } 367 } 368 dest = getHdrVal(MsgTraceDest) 369 // Check the destination to see if this is a valid public subject. 370 if !IsValidPublishSubject(dest) { 371 // We still have to return a msgTrace object (if traceOnly is set) 372 // because if we don't, the message will end-up being delivered to 373 // applications, which may break them. We report the error in any case. 374 c.Errorf("Destination %q is not valid, won't be able to trace events", dest) 375 if !traceOnly { 376 // We can bail, tracing will be disabled for this message. 377 return nil 378 } 379 } 380 } 381 var ( 382 // Account to use when sending the trace event 383 acc *Account 384 // Ingress' account name 385 ian string 386 // Origin account name 387 oan string 388 // The hop "id", taken from headers only when not from CLIENT 389 hop string 390 ) 391 if c.kind == ROUTER || c.kind == GATEWAY || c.kind == LEAF { 392 // The ingress account name will always be c.pa.account, but `acc` may 393 // be different if we have an origin account header. 394 if c.kind == LEAF { 395 ian = c.acc.GetName() 396 } else { 397 ian = string(c.pa.account) 398 } 399 // The remote will have set the origin account header only if the 400 // message changed account (think of service imports). 401 oan = getHdrVal(MsgTraceOriginAccount) 402 if oan == _EMPTY_ { 403 // For LEAF or ROUTER with pinned-account, we can use the c.acc. 404 if c.kind == LEAF || (c.kind == ROUTER && len(c.route.accName) > 0) { 405 acc = c.acc 406 } else { 407 // We will lookup account with c.pa.account (or ian). 408 oan = ian 409 } 410 } 411 // Unless we already got the account, we need to look it up. 412 if acc == nil { 413 // We don't want to do account resolving here. 414 if acci, ok := c.srv.accounts.Load(oan); ok { 415 acc = acci.(*Account) 416 // Since we have looked-up the account, we don't need oan, so 417 // clear it in case it was set. 418 oan = _EMPTY_ 419 } else { 420 // We still have to return a msgTrace object (if traceOnly is set) 421 // because if we don't, the message will end-up being delivered to 422 // applications, which may break them. We report the error in any case. 423 c.Errorf("Account %q was not found, won't be able to trace events", oan) 424 if !traceOnly { 425 // We can bail, tracing will be disabled for this message. 426 return nil 427 } 428 } 429 } 430 // Check the hop header 431 hop = getHdrVal(MsgTraceHop) 432 } else { 433 acc = c.acc 434 ian = acc.GetName() 435 } 436 // If external, we need to have the account's trace destination set, 437 // otherwise, we are not enabling tracing. 438 if external { 439 var sampling int 440 if acc != nil { 441 dest, sampling = acc.getTraceDestAndSampling() 442 } 443 if dest == _EMPTY_ { 444 // No account destination, no tracing for external trace headers. 445 return nil 446 } 447 // Check sampling, but only from origin server. 448 if c.kind == CLIENT && !sample(sampling) { 449 // Need to desactivate the traceParentHdr so that if the message 450 // is routed, it does possibly trigger a trace there. 451 disableTraceHeaders(c, hdr) 452 return nil 453 } 454 } 455 c.pa.trace = &msgTrace{ 456 srv: c.srv, 457 acc: acc, 458 oan: oan, 459 dest: dest, 460 ct: ct, 461 hop: hop, 462 event: &MsgTraceEvent{ 463 Request: MsgTraceRequest{ 464 Header: headers, 465 MsgSize: c.pa.size, 466 }, 467 Events: append(MsgTraceEvents(nil), &MsgTraceIngress{ 468 MsgTraceBase: MsgTraceBase{ 469 Type: MsgTraceIngressType, 470 Timestamp: time.Now(), 471 }, 472 Kind: c.kind, 473 CID: c.cid, 474 Name: getConnName(c), 475 Account: ian, 476 Subject: string(c.pa.subject), 477 }), 478 }, 479 tonly: traceOnly, 480 } 481 return c.pa.trace 482 } 483 484 func sample(sampling int) bool { 485 // Option parsing should ensure that sampling is [1..100], but consider 486 // any value outside of this range to be 100%. 487 if sampling <= 0 || sampling >= 100 { 488 return true 489 } 490 return rand.Int31n(100) <= int32(sampling) 491 } 492 493 // This function will return the header as a map (instead of http.Header because 494 // we want to preserve the header names' case) and a boolean that indicates if 495 // the headers have been lifted due to the presence of the external trace header 496 // only. 497 // Note that because of the traceParentHdr, the search is done in a case 498 // insensitive way, but if the header is found, it is rewritten in lower case 499 // as suggested by the spec, but also to make it easier to disable the header 500 // when needed. 501 func genHeaderMapIfTraceHeadersPresent(hdr []byte) (map[string][]string, bool) { 502 503 var ( 504 _keys = [64][]byte{} 505 _vals = [64][]byte{} 506 m map[string][]string 507 traceDestHdrFound bool 508 traceParentHdrFound bool 509 ) 510 // Skip the hdrLine 511 if !bytes.HasPrefix(hdr, stringToBytes(hdrLine)) { 512 return nil, false 513 } 514 515 traceDestHdrAsBytes := stringToBytes(MsgTraceDest) 516 traceParentHdrAsBytes := stringToBytes(traceParentHdr) 517 crLFAsBytes := stringToBytes(CR_LF) 518 dashAsBytes := stringToBytes("-") 519 520 keys := _keys[:0] 521 vals := _vals[:0] 522 523 for i := len(hdrLine); i < len(hdr); { 524 // Search for key/val delimiter 525 del := bytes.IndexByte(hdr[i:], ':') 526 if del < 0 { 527 break 528 } 529 keyStart := i 530 key := hdr[keyStart : keyStart+del] 531 i += del + 1 532 valStart := i 533 nl := bytes.Index(hdr[valStart:], crLFAsBytes) 534 if nl < 0 { 535 break 536 } 537 if len(key) > 0 { 538 val := bytes.Trim(hdr[valStart:valStart+nl], " \t") 539 vals = append(vals, val) 540 541 // Check for the external trace header. 542 if bytes.EqualFold(key, traceParentHdrAsBytes) { 543 // Rewrite the header using lower case if needed. 544 if !bytes.Equal(key, traceParentHdrAsBytes) { 545 copy(hdr[keyStart:], traceParentHdrAsBytes) 546 } 547 // We will now check if the value has sampling or not. 548 // TODO(ik): Not sure if this header can have multiple values 549 // or not, and if so, what would be the rule to check for 550 // sampling. What is done here is to check them all until we 551 // found one with sampling. 552 if !traceParentHdrFound { 553 tk := bytes.Split(val, dashAsBytes) 554 if len(tk) == 4 && len([]byte(tk[3])) == 2 { 555 if hexVal, err := strconv.ParseInt(bytesToString(tk[3]), 16, 8); err == nil { 556 if hexVal&0x1 == 0x1 { 557 traceParentHdrFound = true 558 } 559 } 560 } 561 } 562 // Add to the keys with the external trace header in lower case. 563 keys = append(keys, traceParentHdrAsBytes) 564 } else { 565 // Is the key the Nats-Trace-Dest header? 566 if bytes.EqualFold(key, traceDestHdrAsBytes) { 567 traceDestHdrFound = true 568 } 569 // Add to the keys and preserve the key's case 570 keys = append(keys, key) 571 } 572 } 573 i += nl + 2 574 } 575 if !traceDestHdrFound && !traceParentHdrFound { 576 return nil, false 577 } 578 m = make(map[string][]string, len(keys)) 579 for i, k := range keys { 580 hname := string(k) 581 m[hname] = append(m[hname], string(vals[i])) 582 } 583 return m, !traceDestHdrFound && traceParentHdrFound 584 } 585 586 // Special case where we create a trace event before parsing the message. 587 // This is for cases where the connection will be closed when detecting 588 // an error during early message processing (for instance max payload). 589 func (c *client) initAndSendIngressErrEvent(hdr []byte, dest string, ingressError error) { 590 if ingressError == nil { 591 return 592 } 593 ct := getAcceptEncoding(hdr) 594 t := &msgTrace{ 595 srv: c.srv, 596 acc: c.acc, 597 dest: dest, 598 ct: ct, 599 event: &MsgTraceEvent{ 600 Request: MsgTraceRequest{MsgSize: c.pa.size}, 601 Events: append(MsgTraceEvents(nil), &MsgTraceIngress{ 602 MsgTraceBase: MsgTraceBase{ 603 Type: MsgTraceIngressType, 604 Timestamp: time.Now(), 605 }, 606 Kind: c.kind, 607 CID: c.cid, 608 Name: getConnName(c), 609 Error: ingressError.Error(), 610 }), 611 }, 612 } 613 t.sendEvent() 614 } 615 616 // Returns `true` if message tracing is enabled and we are tracing only, 617 // that is, we are not going to deliver the inbound message, returns 618 // `false` otherwise (no tracing, or tracing and message delivery). 619 func (t *msgTrace) traceOnly() bool { 620 return t != nil && t.tonly 621 } 622 623 func (t *msgTrace) setOriginAccountHeaderIfNeeded(c *client, acc *Account, msg []byte) []byte { 624 var oan string 625 // If t.acc is set, only check that, not t.oan. 626 if t.acc != nil { 627 if t.acc != acc { 628 oan = t.acc.GetName() 629 } 630 } else if t.oan != acc.GetName() { 631 oan = t.oan 632 } 633 if oan != _EMPTY_ { 634 msg = c.setHeader(MsgTraceOriginAccount, oan, msg) 635 } 636 return msg 637 } 638 639 func (t *msgTrace) setHopHeader(c *client, msg []byte) []byte { 640 e := t.event 641 e.Hops++ 642 if len(t.hop) > 0 { 643 t.nhop = fmt.Sprintf("%s.%d", t.hop, e.Hops) 644 } else { 645 t.nhop = fmt.Sprintf("%d", e.Hops) 646 } 647 return c.setHeader(MsgTraceHop, t.nhop, msg) 648 } 649 650 // Will look for the MsgTraceSendTo and traceParentHdr headers and change the first 651 // character to an 'X' so that if this message is sent to a remote, the remote 652 // will not initialize tracing since it won't find the actual trace headers. 653 // The function returns the position of the headers so it can efficiently be 654 // re-enabled by calling enableTraceHeaders. 655 // Note that if `msg` can be either the header alone or the full message 656 // (header and payload). This function will use c.pa.hdr to limit the 657 // search to the header section alone. 658 func disableTraceHeaders(c *client, msg []byte) []int { 659 // Code largely copied from getHeader(), except that we don't need the value 660 if c.pa.hdr <= 0 { 661 return []int{-1, -1} 662 } 663 hdr := msg[:c.pa.hdr] 664 headers := [2]string{MsgTraceDest, traceParentHdr} 665 positions := [2]int{-1, -1} 666 for i := 0; i < 2; i++ { 667 key := stringToBytes(headers[i]) 668 pos := bytes.Index(hdr, key) 669 if pos < 0 { 670 continue 671 } 672 // Make sure this key does not have additional prefix. 673 if pos < 2 || hdr[pos-1] != '\n' || hdr[pos-2] != '\r' { 674 continue 675 } 676 index := pos + len(key) 677 if index >= len(hdr) { 678 continue 679 } 680 if hdr[index] != ':' { 681 continue 682 } 683 // Disable the trace by altering the first character of the header 684 hdr[pos] = 'X' 685 positions[i] = pos 686 } 687 // Return the positions of those characters so we can re-enable the headers. 688 return positions[:2] 689 } 690 691 // Changes back the character at the given position `pos` in the `msg` 692 // byte slice to the first character of the MsgTraceSendTo header. 693 func enableTraceHeaders(msg []byte, positions []int) { 694 firstChar := [2]byte{MsgTraceDest[0], traceParentHdr[0]} 695 for i, pos := range positions { 696 if pos == -1 { 697 continue 698 } 699 msg[pos] = firstChar[i] 700 } 701 } 702 703 func (t *msgTrace) setIngressError(err string) { 704 if i := t.event.Ingress(); i != nil { 705 i.Error = err 706 } 707 } 708 709 func (t *msgTrace) addSubjectMappingEvent(subj []byte) { 710 if t == nil { 711 return 712 } 713 t.event.Events = append(t.event.Events, &MsgTraceSubjectMapping{ 714 MsgTraceBase: MsgTraceBase{ 715 Type: MsgTraceSubjectMappingType, 716 Timestamp: time.Now(), 717 }, 718 MappedTo: string(subj), 719 }) 720 } 721 722 func (t *msgTrace) addEgressEvent(dc *client, sub *subscription, err string) { 723 if t == nil { 724 return 725 } 726 e := &MsgTraceEgress{ 727 MsgTraceBase: MsgTraceBase{ 728 Type: MsgTraceEgressType, 729 Timestamp: time.Now(), 730 }, 731 Kind: dc.kind, 732 CID: dc.cid, 733 Name: getConnName(dc), 734 Hop: t.nhop, 735 Error: err, 736 } 737 t.nhop = _EMPTY_ 738 // Specific to CLIENT connections... 739 if dc.kind == CLIENT { 740 // Set the subscription's subject and possibly queue name. 741 e.Subscription = string(sub.subject) 742 if len(sub.queue) > 0 { 743 e.Queue = string(sub.queue) 744 } 745 } 746 if dc.kind == CLIENT || dc.kind == LEAF { 747 if i := t.event.Ingress(); i != nil { 748 // If the Ingress' account is different from the destination's 749 // account, add the account name into the Egress trace event. 750 // This would happen with service imports. 751 if dcAccName := dc.acc.GetName(); dcAccName != i.Account { 752 e.Account = dcAccName 753 } 754 } 755 } 756 t.event.Events = append(t.event.Events, e) 757 } 758 759 func (t *msgTrace) addStreamExportEvent(dc *client, to []byte) { 760 if t == nil { 761 return 762 } 763 dc.mu.Lock() 764 accName := dc.acc.GetName() 765 dc.mu.Unlock() 766 t.event.Events = append(t.event.Events, &MsgTraceStreamExport{ 767 MsgTraceBase: MsgTraceBase{ 768 Type: MsgTraceStreamExportType, 769 Timestamp: time.Now(), 770 }, 771 Account: accName, 772 To: string(to), 773 }) 774 } 775 776 func (t *msgTrace) addServiceImportEvent(accName, from, to string) { 777 if t == nil { 778 return 779 } 780 t.event.Events = append(t.event.Events, &MsgTraceServiceImport{ 781 MsgTraceBase: MsgTraceBase{ 782 Type: MsgTraceServiceImportType, 783 Timestamp: time.Now(), 784 }, 785 Account: accName, 786 From: from, 787 To: to, 788 }) 789 } 790 791 func (t *msgTrace) addJetStreamEvent(streamName string) { 792 if t == nil { 793 return 794 } 795 t.js = &MsgTraceJetStream{ 796 MsgTraceBase: MsgTraceBase{ 797 Type: MsgTraceJetStreamType, 798 Timestamp: time.Now(), 799 }, 800 Stream: streamName, 801 } 802 t.event.Events = append(t.event.Events, t.js) 803 } 804 805 func (t *msgTrace) updateJetStreamEvent(subject string, noInterest bool) { 806 if t == nil { 807 return 808 } 809 // JetStream event should have been created in addJetStreamEvent 810 if t.js == nil { 811 return 812 } 813 t.js.Subject = subject 814 t.js.NoInterest = noInterest 815 // Update the timestamp since this is more accurate than when it 816 // was first added in addJetStreamEvent(). 817 t.js.Timestamp = time.Now() 818 } 819 820 func (t *msgTrace) sendEventFromJetStream(err error) { 821 if t == nil { 822 return 823 } 824 // JetStream event should have been created in addJetStreamEvent 825 if t.js == nil { 826 return 827 } 828 if err != nil { 829 t.js.Error = err.Error() 830 } 831 t.sendEvent() 832 } 833 834 func (t *msgTrace) sendEvent() { 835 if t == nil { 836 return 837 } 838 if t.js != nil { 839 ready := atomic.AddInt32(&t.ready, 1) == 2 840 if !ready { 841 return 842 } 843 } 844 t.srv.sendInternalAccountSysMsg(t.acc, t.dest, &t.event.Server, t.event, t.ct) 845 }